EDAboard.com | EDAboard.de | EDAboard.co.uk | WTWH Media

elektroda.net NewsGroups Forum Index - VHDL Language - **automatic indexing for bus assembly in VHDL**

Guest

Tue Sep 04, 2018 6:45 pm

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

Guest

Tue Sep 04, 2018 9:45 pm

On Tuesday, September 4, 2018 at 1:42:19 PM UTC-4, glenn.c...@gmail.com wrote:

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

Do you have a copy of The Designer's Guide to VHDL by Peter Ashenden? It's the "VHDL Bible". In the 3rd Edition, refer to section 4.1.3 Array Attributes.

I haven't done exactly what you're looking for, but I regularly use 'length attribute to reduce the amount of code that needs to be updated or rewritten when an array width (or length) changes, such as a data bus. To make my code more reusable, it's pretty standard to use a Generic to define the bus width and then it just cascades through the rest of the module design without any additional intervention.

Guest

Wed Sep 05, 2018 6:45 pm

On Tuesday, September 4, 2018 at 9:35:35 PM UTC+1, KKoorndyk wrote:

On Tuesday, September 4, 2018 at 1:42:19 PM UTC-4, glenn.c...@gmail.com wrote:

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals.. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

Do you have a copy of The Designer's Guide to VHDL by Peter Ashenden? It's the "VHDL Bible". In the 3rd Edition, refer to section 4.1.3 Array Attributes.

I haven't done exactly what you're looking for, but I regularly use 'length attribute to reduce the amount of code that needs to be updated or rewritten when an array width (or length) changes, such as a data bus. To make my code more reusable, it's pretty standard to use a Generic to define the bus width and then it just cascades through the rest of the module design without any additional intervention.

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals.. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

Do you have a copy of The Designer's Guide to VHDL by Peter Ashenden? It's the "VHDL Bible". In the 3rd Edition, refer to section 4.1.3 Array Attributes.

I haven't done exactly what you're looking for, but I regularly use 'length attribute to reduce the amount of code that needs to be updated or rewritten when an array width (or length) changes, such as a data bus. To make my code more reusable, it's pretty standard to use a Generic to define the bus width and then it just cascades through the rest of the module design without any additional intervention.

Thanks for the reply. Yes, I do have a copy of Ashenden, and I can see that attributes are generally very useful - I just mentioned them as an example in my original post though.

I suspect my problem is due to the synthesis tool (Vivado in this case) failing to recognise that the assignments to the bus are actually static (as in my first example) and inferring logic for this. No doubt this is not helped by my use of a variable to keep track of the index value, and the combinatorial process.

In the end I managed to achieve what I wanted, at least for this simple example case, using a procedure:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(9 downto 0) := (others => '0')

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

procedure bus_assemble ( x, y, z : std_logic_vector; signal outbus : out std_logic_vector) is

variable bus_ind : integer := 0;

begin

outbus(bus_ind+x'length-1 downto bus_ind) <= x;

bus_ind := bus_ind + x'length;

outbus(bus_ind+y'length-1 downto bus_ind) <= y;

bus_ind := bus_ind + y'length;

outbus(bus_ind+z'length-1 downto bus_ind) <= z;

end bus_assemble;

begin

-- Bus assembly ----

bus_assemble ( b, c, d, bus_out);

end rtl;

******************************************************************

Unfortunately, though, this will not help in the general case where I have an arbitrary number of signals to concatenate onto the bus, unless there is a way to collect together multiple vectors of differing lengths in kind of array ...

I suspect I will just have to use the regular concatenation operator, and calculate the necessary size of the bus by adding together the lengths of the individual signals.

Thanks again for you help.

Guest

Thu Sep 06, 2018 2:45 pm

On Wednesday, September 5, 2018 at 7:48:47 PM UTC+1, Rob Gaddi wrote:

On 09/05/2018 10:38 AM, glennchid wrote:

On Tuesday, September 4, 2018 at 9:35:35 PM UTC+1, KKoorndyk wrote:

On Tuesday, September 4, 2018 at 1:42:19 PM UTC-4, glenn.c...@gmail.com wrote:

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures.. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

Do you have a copy of The Designer's Guide to VHDL by Peter Ashenden? It's the "VHDL Bible". In the 3rd Edition, refer to section 4.1.3 Array Attributes.

I haven't done exactly what you're looking for, but I regularly use 'length attribute to reduce the amount of code that needs to be updated or rewritten when an array width (or length) changes, such as a data bus. To make my code more reusable, it's pretty standard to use a Generic to define the bus width and then it just cascades through the rest of the module design without any additional intervention.

Thanks for the reply. Yes, I do have a copy of Ashenden, and I can see that attributes are generally very useful - I just mentioned them as an example in my original post though.

I suspect my problem is due to the synthesis tool (Vivado in this case) failing to recognise that the assignments to the bus are actually static (as in my first example) and inferring logic for this. No doubt this is not helped by my use of a variable to keep track of the index value, and the combinatorial process.

In the end I managed to achieve what I wanted, at least for this simple example case, using a procedure:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(9 downto 0) := (others => '0')

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

procedure bus_assemble ( x, y, z : std_logic_vector; signal outbus : out std_logic_vector) is

variable bus_ind : integer := 0;

begin

outbus(bus_ind+x'length-1 downto bus_ind) <= x;

bus_ind := bus_ind + x'length;

outbus(bus_ind+y'length-1 downto bus_ind) <= y;

bus_ind := bus_ind + y'length;

outbus(bus_ind+z'length-1 downto bus_ind) <= z;

end bus_assemble;

begin

-- Bus assembly ----

bus_assemble ( b, c, d, bus_out);

end rtl;

******************************************************************

Unfortunately, though, this will not help in the general case where I have an arbitrary number of signals to concatenate onto the bus, unless there is a way to collect together multiple vectors of differing lengths in kind of array ...

I suspect I will just have to use the regular concatenation operator, and calculate the necessary size of the bus by adding together the lengths of the individual signals.

Thanks again for you help.

I can't think of anything to help the general case; you can't really

iterate over "ragged arrays" in VHDL, because there isn't really a

concept of such. VHDL-2018 will let you iterate over record types, but

that doesn't help you right now.

One thing I tend to do fairly often is to create record types for my

structured data and create custom pack/unpack functions that turn the

entire record into std_logic_vector and back. To build up those

pack/unpacks, I use the following code; which I freely invite you or

anyone else to have at:

---

-------------------------------------------------------------------------------

-- Title : Useful Standard Functions Library

-------------------------------------------------------------------------------

-- File : standard_functions.vhd

-- Author : Rob Gaddi <rgaddi_at_highlandtechnology.com

-- Company : Highland Technology, Inc.

-- Created : 14-Feb-2012

-- Last update: 14-Feb-2012

-- Platform : Independent

-- Standard : VHDL 93 - 08

-------------------------------------------------------------------------------

-- Description: Useful standard functions.

-------------------------------------------------------------------------------

-- Revision History:

-------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;

use ieee.numeric_std.all;

package standard_functions is

-----------------------------------------------------------------------

-- Base 2 Logarithms

-----------------------------------------------------------------------

--! ceil(log2(x))

--! This is also the number of bits required to represent

--! x possible choices. clog2(4) = 2, clog2(5) = 3

pure function clog2(x : in positive) return natural;

--! floor(log2(x))

--! This is also the index (with the LSB as bit 0) of the

--! uppermost 1 bit for a given number represented in

--! binary. flog2(4) = 2, flog2(5) = 2

pure function flog2(x : in positive) return natural;

--! Number of bits needed to represent a given number in

--! binary form. nbits(4) = 3, nbits(5) = 3, nbits(-4) = 4

pure function nbits(x : in integer) return positive;

-----------------------------------------------------------------------

-- Conversion Functions

-----------------------------------------------------------------------

--! Converts an integer straight into a std_logic_vector.

pure function TO_SLV(data : in integer; bits : in positive) return

std_logic_vector;

--! Converts a logic value to std_logic using active-high rules.

pure function POS_LOGIC(x : in boolean) return std_logic;

--! Converts a logic value to std_logic using active-low rules.

pure function NEG_LOGIC(x : in boolean) return std_logic;

-----------------------------------------------------------------------

-- Record conversion functions

-----------------------------------------------------------------------

-- These are useful when having to turn arbitrary data collections

-- into std_logic_vector, generally for use with IP cores.

--

-- pack is used to line up multiple pieces of data in one

-- std_logic_vector; using a running variable that will reflect

-- the index of the LSB of the next data to write.

--

-- unpack is the reverse, pulling data off a vector.

--

-- These shouldn't be used to construct bitfields that will be

-- made accessible to the outside world; they're really just for

-- packing things up internally. Generally, you'll composite

-- these together to make larger functions that turn an entire

-- record type into and back from a std_logic_vector.

--

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in unsigned

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in signed

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic_vector

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out unsigned

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out signed

);

-----------------------------------------------------------------------

-- Comparison Functions

-----------------------------------------------------------------------

--! Performs a "counter comparison", in which only bits expected

--! to be '1' are actually checked. This is equivalent to a full

--! equality compare when used on an up-counter, but can take

--! substantially less logic because bits expected to be zero

--! aren't checked. For instance, to compare a counter to the

--! constant value 17 requires only 2 bits to be compared.

pure function counter_eq(x : in unsigned; constant tgt : in natural)

return boolean;

pure function counter_eq(x : in natural; constant tgt : in natural)

return boolean;

end package standard_functions;

package body standard_functions is

-----------------------------------------------------------------------

-- Base 2 Logarithms

-----------------------------------------------------------------------

pure function flog2 (x : in positive) return natural is

variable temp, log: natural;

begin

temp := x / 2;

log := 0;

while (temp /= 0) loop

temp := temp/2;

log := log + 1;

end loop;

return log;

end function flog2;

pure function clog2 (x : in positive) return natural is

begin

if (x = 1) then

return 1;

else

return 1 + flog2(x - 1);

end if;

end function clog2;

pure function nbits(x : in integer) return positive is

begin

if (x < 0) then

return 2 + flog2(-x);

elsif (x = 0) then

return 1;

else

return 1 + flog2(x);

end if;

end function nbits;

-----------------------------------------------------------------------

-- Conversion Functions

-----------------------------------------------------------------------

pure function TO_SLV( data : in integer;

bits : in positive)

return std_logic_vector is

begin

if (data < 0) then

return STD_LOGIC_VECTOR(TO_SIGNED(data, bits));

else

return STD_LOGIC_VECTOR(TO_UNSIGNED(data, bits));

end if;

end function TO_SLV;

pure function POS_LOGIC(x : in boolean) return std_logic is

begin

if x then

return '1';

else

return '0';

end if;

end function POS_LOGIC;

--! Converts a logic value to std_logic using active-low rules.

pure function NEG_LOGIC(x : in boolean) return std_logic is

begin

if x then

return '0';

else

return '1';

end if;

end function NEG_LOGIC;

-----------------------------------------------------------------------

-- Record conversion functions

-----------------------------------------------------------------------

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

) is

begin

target(idx + nd'length - 1 downto idx) := nd;

idx := idx + nd'length;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic

) is

begin

target(idx) := nd;

idx := idx + 1;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in unsigned

) is

begin

target(idx + nd'length - 1 downto idx) := STD_LOGIC_VECTOR(nd);

idx := idx + nd'length;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in signed

) is

begin

target(idx + nd'length - 1 downto idx) := STD_LOGIC_VECTOR(nd);

idx := idx + nd'length;

end procedure pack;

----------------------------------------------------------------------

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic_vector

) is

begin

dat := source(idx + dat'length - 1 downto idx);

idx := idx + dat'length;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic

) is

begin

dat := source(idx);

idx := idx + 1;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out unsigned

) is

begin

dat := UNSIGNED(source(idx + dat'length - 1 downto idx));

idx := idx + dat'length;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out signed

) is

begin

dat := SIGNED(source(idx + dat'length - 1 downto idx));

idx := idx + dat'length;

end procedure unpack;

-----------------------------------------------------------------------

-- Comparison Functions

-----------------------------------------------------------------------

pure function counter_eq(x : in unsigned; constant tgt : in natural)

return boolean is

variable target : unsigned(x'range);

begin

target := TO_UNSIGNED(tgt, target'length);

for i in target'range loop

if (target(i) = '1') and (x(i) /= '1') then

return false;

end if;

end loop;

return true;

end function counter_eq;

pure function counter_eq(x : in natural; constant tgt : in natural)

return boolean is

variable counter : unsigned(31 downto 0);

begin

counter := TO_UNSIGNED(x, 32);

return counter_eq(counter, tgt);

end function counter_eq;

end package body standard_functions;

--

Rob Gaddi, Highland Technology -- www.highlandtechnology.com

Email address domain is currently out of order. See above to fix.

On Tuesday, September 4, 2018 at 9:35:35 PM UTC+1, KKoorndyk wrote:

On Tuesday, September 4, 2018 at 1:42:19 PM UTC-4, glenn.c...@gmail.com wrote:

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures.. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

Do you have a copy of The Designer's Guide to VHDL by Peter Ashenden? It's the "VHDL Bible". In the 3rd Edition, refer to section 4.1.3 Array Attributes.

I haven't done exactly what you're looking for, but I regularly use 'length attribute to reduce the amount of code that needs to be updated or rewritten when an array width (or length) changes, such as a data bus. To make my code more reusable, it's pretty standard to use a Generic to define the bus width and then it just cascades through the rest of the module design without any additional intervention.

Thanks for the reply. Yes, I do have a copy of Ashenden, and I can see that attributes are generally very useful - I just mentioned them as an example in my original post though.

I suspect my problem is due to the synthesis tool (Vivado in this case) failing to recognise that the assignments to the bus are actually static (as in my first example) and inferring logic for this. No doubt this is not helped by my use of a variable to keep track of the index value, and the combinatorial process.

In the end I managed to achieve what I wanted, at least for this simple example case, using a procedure:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(9 downto 0) := (others => '0')

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

procedure bus_assemble ( x, y, z : std_logic_vector; signal outbus : out std_logic_vector) is

variable bus_ind : integer := 0;

begin

outbus(bus_ind+x'length-1 downto bus_ind) <= x;

bus_ind := bus_ind + x'length;

outbus(bus_ind+y'length-1 downto bus_ind) <= y;

bus_ind := bus_ind + y'length;

outbus(bus_ind+z'length-1 downto bus_ind) <= z;

end bus_assemble;

begin

-- Bus assembly ----

bus_assemble ( b, c, d, bus_out);

end rtl;

******************************************************************

Unfortunately, though, this will not help in the general case where I have an arbitrary number of signals to concatenate onto the bus, unless there is a way to collect together multiple vectors of differing lengths in kind of array ...

I suspect I will just have to use the regular concatenation operator, and calculate the necessary size of the bus by adding together the lengths of the individual signals.

Thanks again for you help.

I can't think of anything to help the general case; you can't really

iterate over "ragged arrays" in VHDL, because there isn't really a

concept of such. VHDL-2018 will let you iterate over record types, but

that doesn't help you right now.

One thing I tend to do fairly often is to create record types for my

structured data and create custom pack/unpack functions that turn the

entire record into std_logic_vector and back. To build up those

pack/unpacks, I use the following code; which I freely invite you or

anyone else to have at:

---

-------------------------------------------------------------------------------

-- Title : Useful Standard Functions Library

-------------------------------------------------------------------------------

-- File : standard_functions.vhd

-- Author : Rob Gaddi <rgaddi_at_highlandtechnology.com

-- Company : Highland Technology, Inc.

-- Created : 14-Feb-2012

-- Last update: 14-Feb-2012

-- Platform : Independent

-- Standard : VHDL 93 - 08

-------------------------------------------------------------------------------

-- Description: Useful standard functions.

-------------------------------------------------------------------------------

-- Revision History:

-------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;

use ieee.numeric_std.all;

package standard_functions is

-----------------------------------------------------------------------

-- Base 2 Logarithms

-----------------------------------------------------------------------

--! ceil(log2(x))

--! This is also the number of bits required to represent

--! x possible choices. clog2(4) = 2, clog2(5) = 3

pure function clog2(x : in positive) return natural;

--! floor(log2(x))

--! This is also the index (with the LSB as bit 0) of the

--! uppermost 1 bit for a given number represented in

--! binary. flog2(4) = 2, flog2(5) = 2

pure function flog2(x : in positive) return natural;

--! Number of bits needed to represent a given number in

--! binary form. nbits(4) = 3, nbits(5) = 3, nbits(-4) = 4

pure function nbits(x : in integer) return positive;

-----------------------------------------------------------------------

-- Conversion Functions

-----------------------------------------------------------------------

--! Converts an integer straight into a std_logic_vector.

pure function TO_SLV(data : in integer; bits : in positive) return

std_logic_vector;

--! Converts a logic value to std_logic using active-high rules.

pure function POS_LOGIC(x : in boolean) return std_logic;

--! Converts a logic value to std_logic using active-low rules.

pure function NEG_LOGIC(x : in boolean) return std_logic;

-----------------------------------------------------------------------

-- Record conversion functions

-----------------------------------------------------------------------

-- These are useful when having to turn arbitrary data collections

-- into std_logic_vector, generally for use with IP cores.

--

-- pack is used to line up multiple pieces of data in one

-- std_logic_vector; using a running variable that will reflect

-- the index of the LSB of the next data to write.

--

-- unpack is the reverse, pulling data off a vector.

--

-- These shouldn't be used to construct bitfields that will be

-- made accessible to the outside world; they're really just for

-- packing things up internally. Generally, you'll composite

-- these together to make larger functions that turn an entire

-- record type into and back from a std_logic_vector.

--

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in unsigned

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in signed

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic_vector

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out unsigned

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out signed

);

-----------------------------------------------------------------------

-- Comparison Functions

-----------------------------------------------------------------------

--! Performs a "counter comparison", in which only bits expected

--! to be '1' are actually checked. This is equivalent to a full

--! equality compare when used on an up-counter, but can take

--! substantially less logic because bits expected to be zero

--! aren't checked. For instance, to compare a counter to the

--! constant value 17 requires only 2 bits to be compared.

pure function counter_eq(x : in unsigned; constant tgt : in natural)

return boolean;

pure function counter_eq(x : in natural; constant tgt : in natural)

return boolean;

end package standard_functions;

package body standard_functions is

-----------------------------------------------------------------------

-- Base 2 Logarithms

-----------------------------------------------------------------------

pure function flog2 (x : in positive) return natural is

variable temp, log: natural;

begin

temp := x / 2;

log := 0;

while (temp /= 0) loop

temp := temp/2;

log := log + 1;

end loop;

return log;

end function flog2;

pure function clog2 (x : in positive) return natural is

begin

if (x = 1) then

return 1;

else

return 1 + flog2(x - 1);

end if;

end function clog2;

pure function nbits(x : in integer) return positive is

begin

if (x < 0) then

return 2 + flog2(-x);

elsif (x = 0) then

return 1;

else

return 1 + flog2(x);

end if;

end function nbits;

-----------------------------------------------------------------------

-- Conversion Functions

-----------------------------------------------------------------------

pure function TO_SLV( data : in integer;

bits : in positive)

return std_logic_vector is

begin

if (data < 0) then

return STD_LOGIC_VECTOR(TO_SIGNED(data, bits));

else

return STD_LOGIC_VECTOR(TO_UNSIGNED(data, bits));

end if;

end function TO_SLV;

pure function POS_LOGIC(x : in boolean) return std_logic is

begin

if x then

return '1';

else

return '0';

end if;

end function POS_LOGIC;

--! Converts a logic value to std_logic using active-low rules.

pure function NEG_LOGIC(x : in boolean) return std_logic is

begin

if x then

return '0';

else

return '1';

end if;

end function NEG_LOGIC;

-----------------------------------------------------------------------

-- Record conversion functions

-----------------------------------------------------------------------

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

) is

begin

target(idx + nd'length - 1 downto idx) := nd;

idx := idx + nd'length;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic

) is

begin

target(idx) := nd;

idx := idx + 1;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in unsigned

) is

begin

target(idx + nd'length - 1 downto idx) := STD_LOGIC_VECTOR(nd);

idx := idx + nd'length;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in signed

) is

begin

target(idx + nd'length - 1 downto idx) := STD_LOGIC_VECTOR(nd);

idx := idx + nd'length;

end procedure pack;

----------------------------------------------------------------------

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic_vector

) is

begin

dat := source(idx + dat'length - 1 downto idx);

idx := idx + dat'length;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic

) is

begin

dat := source(idx);

idx := idx + 1;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out unsigned

) is

begin

dat := UNSIGNED(source(idx + dat'length - 1 downto idx));

idx := idx + dat'length;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out signed

) is

begin

dat := SIGNED(source(idx + dat'length - 1 downto idx));

idx := idx + dat'length;

end procedure unpack;

-----------------------------------------------------------------------

-- Comparison Functions

-----------------------------------------------------------------------

pure function counter_eq(x : in unsigned; constant tgt : in natural)

return boolean is

variable target : unsigned(x'range);

begin

target := TO_UNSIGNED(tgt, target'length);

for i in target'range loop

if (target(i) = '1') and (x(i) /= '1') then

return false;

end if;

end loop;

return true;

end function counter_eq;

pure function counter_eq(x : in natural; constant tgt : in natural)

return boolean is

variable counter : unsigned(31 downto 0);

begin

counter := TO_UNSIGNED(x, 32);

return counter_eq(counter, tgt);

end function counter_eq;

end package body standard_functions;

--

Rob Gaddi, Highland Technology -- www.highlandtechnology.com

Email address domain is currently out of order. See above to fix.

Many thanks Rob, the functions look very useful. In fact, your 'pack' procedure appears to do exactly what I was originally wanting! It had not occurred to me to define the ports as inout though. The problem though is that the synthesis still infers a lot of logic to do this. I suspect the reason for this is that I need to make the procedure calls from within a process, in order for them to be executed sequentially. Is there another to achieve sequential execution without using a process block? My new code is below, using your 'pack' procedure. Maybe you can see some problem with this?

***********************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0) := (others => '0')

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

) is

begin

target(idx + nd'length - 1 downto idx) := nd;

idx := idx + nd'length;

end procedure pack;

begin

BUS_ASSEMBLE : process

variable bus_tmp : std_logic_vector(5 downto 0) := (others => '0');

variable idx : integer := 0;

begin

bus_tmp := (others => '0');

pack (bus_tmp, idx, b);

pack (bus_tmp, idx, c);

pack (bus_tmp, idx, d);

bus_out(bus_tmp'length-1 downto 0) <= bus_tmp;

wait;

end process BUS_ASSEMBLE;

end rtl;

************************************************************

Guest

Thu Sep 06, 2018 7:45 pm

On Thursday, September 6, 2018 at 5:40:01 PM UTC+1, Rob Gaddi wrote:

On 09/06/2018 06:41 AM, glennchid wrote:

On Wednesday, September 5, 2018 at 7:48:47 PM UTC+1, Rob Gaddi wrote:

On 09/05/2018 10:38 AM, glennchid wrote:

On Tuesday, September 4, 2018 at 9:35:35 PM UTC+1, KKoorndyk wrote:

On Tuesday, September 4, 2018 at 1:42:19 PM UTC-4, glenn.c...@gmail.com wrote:

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

Do you have a copy of The Designer's Guide to VHDL by Peter Ashenden? It's the "VHDL Bible". In the 3rd Edition, refer to section 4.1.3 Array Attributes.

I haven't done exactly what you're looking for, but I regularly use 'length attribute to reduce the amount of code that needs to be updated or rewritten when an array width (or length) changes, such as a data bus. To make my code more reusable, it's pretty standard to use a Generic to define the bus width and then it just cascades through the rest of the module design without any additional intervention.

Thanks for the reply. Yes, I do have a copy of Ashenden, and I can see that attributes are generally very useful - I just mentioned them as an example in my original post though.

I suspect my problem is due to the synthesis tool (Vivado in this case) failing to recognise that the assignments to the bus are actually static (as in my first example) and inferring logic for this. No doubt this is not helped by my use of a variable to keep track of the index value, and the combinatorial process.

In the end I managed to achieve what I wanted, at least for this simple example case, using a procedure:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(9 downto 0) := (others => '0')

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

procedure bus_assemble ( x, y, z : std_logic_vector; signal outbus : out std_logic_vector) is

variable bus_ind : integer := 0;

begin

outbus(bus_ind+x'length-1 downto bus_ind) <= x;

bus_ind := bus_ind + x'length;

outbus(bus_ind+y'length-1 downto bus_ind) <= y;

bus_ind := bus_ind + y'length;

outbus(bus_ind+z'length-1 downto bus_ind) <= z;

end bus_assemble;

begin

-- Bus assembly ----

bus_assemble ( b, c, d, bus_out);

end rtl;

******************************************************************

Unfortunately, though, this will not help in the general case where I have an arbitrary number of signals to concatenate onto the bus, unless there is a way to collect together multiple vectors of differing lengths in kind of array ...

I suspect I will just have to use the regular concatenation operator, and calculate the necessary size of the bus by adding together the lengths of the individual signals.

Thanks again for you help.

I can't think of anything to help the general case; you can't really

iterate over "ragged arrays" in VHDL, because there isn't really a

concept of such. VHDL-2018 will let you iterate over record types, but

that doesn't help you right now.

One thing I tend to do fairly often is to create record types for my

structured data and create custom pack/unpack functions that turn the

entire record into std_logic_vector and back. To build up those

pack/unpacks, I use the following code; which I freely invite you or

anyone else to have at:

---

-------------------------------------------------------------------------------

-- Title : Useful Standard Functions Library

-------------------------------------------------------------------------------

-- File : standard_functions.vhd

-- Author : Rob Gaddi <rgaddi_at_highlandtechnology.com

-- Company : Highland Technology, Inc.

-- Created : 14-Feb-2012

-- Last update: 14-Feb-2012

-- Platform : Independent

-- Standard : VHDL 93 - 08

-------------------------------------------------------------------------------

-- Description: Useful standard functions.

-------------------------------------------------------------------------------

-- Revision History:

-------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;

use ieee.numeric_std.all;

package standard_functions is

-----------------------------------------------------------------------

-- Base 2 Logarithms

-----------------------------------------------------------------------

--! ceil(log2(x))

--! This is also the number of bits required to represent

--! x possible choices. clog2(4) = 2, clog2(5) = 3

pure function clog2(x : in positive) return natural;

--! floor(log2(x))

--! This is also the index (with the LSB as bit 0) of the

--! uppermost 1 bit for a given number represented in

--! binary. flog2(4) = 2, flog2(5) = 2

pure function flog2(x : in positive) return natural;

--! Number of bits needed to represent a given number in

--! binary form. nbits(4) = 3, nbits(5) = 3, nbits(-4) = 4

pure function nbits(x : in integer) return positive;

-----------------------------------------------------------------------

-- Conversion Functions

-----------------------------------------------------------------------

--! Converts an integer straight into a std_logic_vector.

pure function TO_SLV(data : in integer; bits : in positive) return

std_logic_vector;

--! Converts a logic value to std_logic using active-high rules.

pure function POS_LOGIC(x : in boolean) return std_logic;

--! Converts a logic value to std_logic using active-low rules.

pure function NEG_LOGIC(x : in boolean) return std_logic;

-----------------------------------------------------------------------

-- Record conversion functions

-----------------------------------------------------------------------

-- These are useful when having to turn arbitrary data collections

-- into std_logic_vector, generally for use with IP cores.

--

-- pack is used to line up multiple pieces of data in one

-- std_logic_vector; using a running variable that will reflect

-- the index of the LSB of the next data to write.

--

-- unpack is the reverse, pulling data off a vector.

--

-- These shouldn't be used to construct bitfields that will be

-- made accessible to the outside world; they're really just for

-- packing things up internally. Generally, you'll composite

-- these together to make larger functions that turn an entire

-- record type into and back from a std_logic_vector.

--

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in unsigned

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in signed

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic_vector

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out unsigned

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out signed

);

-----------------------------------------------------------------------

-- Comparison Functions

-----------------------------------------------------------------------

--! Performs a "counter comparison", in which only bits expected

--! to be '1' are actually checked. This is equivalent to a full

--! equality compare when used on an up-counter, but can take

--! substantially less logic because bits expected to be zero

--! aren't checked. For instance, to compare a counter to the

--! constant value 17 requires only 2 bits to be compared.

pure function counter_eq(x : in unsigned; constant tgt : in natural)

return boolean;

pure function counter_eq(x : in natural; constant tgt : in natural)

return boolean;

end package standard_functions;

package body standard_functions is

-----------------------------------------------------------------------

-- Base 2 Logarithms

-----------------------------------------------------------------------

pure function flog2 (x : in positive) return natural is

variable temp, log: natural;

begin

temp := x / 2;

log := 0;

while (temp /= 0) loop

temp := temp/2;

log := log + 1;

end loop;

return log;

end function flog2;

pure function clog2 (x : in positive) return natural is

begin

if (x = 1) then

return 1;

else

return 1 + flog2(x - 1);

end if;

end function clog2;

pure function nbits(x : in integer) return positive is

begin

if (x < 0) then

return 2 + flog2(-x);

elsif (x = 0) then

return 1;

else

return 1 + flog2(x);

end if;

end function nbits;

-----------------------------------------------------------------------

-- Conversion Functions

-----------------------------------------------------------------------

pure function TO_SLV( data : in integer;

bits : in positive)

return std_logic_vector is

begin

if (data < 0) then

return STD_LOGIC_VECTOR(TO_SIGNED(data, bits));

else

return STD_LOGIC_VECTOR(TO_UNSIGNED(data, bits));

end if;

end function TO_SLV;

pure function POS_LOGIC(x : in boolean) return std_logic is

begin

if x then

return '1';

else

return '0';

end if;

end function POS_LOGIC;

--! Converts a logic value to std_logic using active-low rules.

pure function NEG_LOGIC(x : in boolean) return std_logic is

begin

if x then

return '0';

else

return '1';

end if;

end function NEG_LOGIC;

-----------------------------------------------------------------------

-- Record conversion functions

-----------------------------------------------------------------------

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

) is

begin

target(idx + nd'length - 1 downto idx) := nd;

idx := idx + nd'length;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic

) is

begin

target(idx) := nd;

idx := idx + 1;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in unsigned

) is

begin

target(idx + nd'length - 1 downto idx) := STD_LOGIC_VECTOR(nd);

idx := idx + nd'length;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in signed

) is

begin

target(idx + nd'length - 1 downto idx) := STD_LOGIC_VECTOR(nd);

idx := idx + nd'length;

end procedure pack;

----------------------------------------------------------------------

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic_vector

) is

begin

dat := source(idx + dat'length - 1 downto idx);

idx := idx + dat'length;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic

) is

begin

dat := source(idx);

idx := idx + 1;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out unsigned

) is

begin

dat := UNSIGNED(source(idx + dat'length - 1 downto idx));

idx := idx + dat'length;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out signed

) is

begin

dat := SIGNED(source(idx + dat'length - 1 downto idx));

idx := idx + dat'length;

end procedure unpack;

-----------------------------------------------------------------------

-- Comparison Functions

-----------------------------------------------------------------------

pure function counter_eq(x : in unsigned; constant tgt : in natural)

return boolean is

variable target : unsigned(x'range);

begin

target := TO_UNSIGNED(tgt, target'length);

for i in target'range loop

if (target(i) = '1') and (x(i) /= '1') then

return false;

end if;

end loop;

return true;

end function counter_eq;

pure function counter_eq(x : in natural; constant tgt : in natural)

return boolean is

variable counter : unsigned(31 downto 0);

begin

counter := TO_UNSIGNED(x, 32);

return counter_eq(counter, tgt);

end function counter_eq;

end package body standard_functions;

--

Rob Gaddi, Highland Technology -- www.highlandtechnology.com

Email address domain is currently out of order. See above to fix.

Many thanks Rob, the functions look very useful. In fact, your 'pack' procedure appears to do exactly what I was originally wanting! It had not occurred to me to define the ports as inout though. The problem though is that the synthesis still infers a lot of logic to do this. I suspect the reason for this is that I need to make the procedure calls from within a process, in order for them to be executed sequentially. Is there another to achieve sequential execution without using a process block? My new code is below, using your 'pack' procedure. Maybe you can see some problem with this?

***********************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0) := (others => '0')

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

) is

begin

target(idx + nd'length - 1 downto idx) := nd;

idx := idx + nd'length;

end procedure pack;

begin

BUS_ASSEMBLE : process

variable bus_tmp : std_logic_vector(5 downto 0) := (others => '0');

variable idx : integer := 0;

begin

bus_tmp := (others => '0');

pack (bus_tmp, idx, b);

pack (bus_tmp, idx, c);

pack (bus_tmp, idx, d);

bus_out(bus_tmp'length-1 downto 0) <= bus_tmp;

wait;

end process BUS_ASSEMBLE;

end rtl;

************************************************************

Your procedure only sets idx to 0 at the beginning of time, not at the

beginning of each execution. Then again, your wait waits for all of

time, so that's not helping you either.

Rather than write a process, you could write it as a function.

function pack_vectors(b, c, d: std_logic_vector) return std_logic_vector

variable bus_tmp : std_logic_vector(5 downto 0) := (others => '0');

variable idx : integer := 0;

begin

pack(bus_tmp, idx, b);

pack(bus_tmp, idx, c);

pack(bus_tmp, idx, d);

return bus_tmp;

end function pack_vectors;

...

bus_out <= pack_vectors(b, c, d);

But your process-based approach should work too.

BUS_ASSEMBLE : process(b, c, d)

variable bus_tmp : std_logic_vector(bus_out'range);

variable idx : integer;

begin

bus_tmp := (others => '0');

idx := 0;

pack (bus_tmp, idx, b);

pack (bus_tmp, idx, c);

pack (bus_tmp, idx, d);

bus_out <= bus_tmp;

end process BUS_ASSEMBLE;

--

Rob Gaddi, Highland Technology -- www.highlandtechnology.com

Email address domain is currently out of order. See above to fix.

On Wednesday, September 5, 2018 at 7:48:47 PM UTC+1, Rob Gaddi wrote:

On 09/05/2018 10:38 AM, glennchid wrote:

On Tuesday, September 4, 2018 at 9:35:35 PM UTC+1, KKoorndyk wrote:

On Tuesday, September 4, 2018 at 1:42:19 PM UTC-4, glenn.c...@gmail.com wrote:

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to understand how to do this in the manner above.

Many thanks for your help,

Glenn.

Do you have a copy of The Designer's Guide to VHDL by Peter Ashenden? It's the "VHDL Bible". In the 3rd Edition, refer to section 4.1.3 Array Attributes.

I haven't done exactly what you're looking for, but I regularly use 'length attribute to reduce the amount of code that needs to be updated or rewritten when an array width (or length) changes, such as a data bus. To make my code more reusable, it's pretty standard to use a Generic to define the bus width and then it just cascades through the rest of the module design without any additional intervention.

Thanks for the reply. Yes, I do have a copy of Ashenden, and I can see that attributes are generally very useful - I just mentioned them as an example in my original post though.

I suspect my problem is due to the synthesis tool (Vivado in this case) failing to recognise that the assignments to the bus are actually static (as in my first example) and inferring logic for this. No doubt this is not helped by my use of a variable to keep track of the index value, and the combinatorial process.

In the end I managed to achieve what I wanted, at least for this simple example case, using a procedure:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(9 downto 0) := (others => '0')

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

procedure bus_assemble ( x, y, z : std_logic_vector; signal outbus : out std_logic_vector) is

variable bus_ind : integer := 0;

begin

outbus(bus_ind+x'length-1 downto bus_ind) <= x;

bus_ind := bus_ind + x'length;

outbus(bus_ind+y'length-1 downto bus_ind) <= y;

bus_ind := bus_ind + y'length;

outbus(bus_ind+z'length-1 downto bus_ind) <= z;

end bus_assemble;

begin

-- Bus assembly ----

bus_assemble ( b, c, d, bus_out);

end rtl;

******************************************************************

Unfortunately, though, this will not help in the general case where I have an arbitrary number of signals to concatenate onto the bus, unless there is a way to collect together multiple vectors of differing lengths in kind of array ...

I suspect I will just have to use the regular concatenation operator, and calculate the necessary size of the bus by adding together the lengths of the individual signals.

Thanks again for you help.

I can't think of anything to help the general case; you can't really

iterate over "ragged arrays" in VHDL, because there isn't really a

concept of such. VHDL-2018 will let you iterate over record types, but

that doesn't help you right now.

One thing I tend to do fairly often is to create record types for my

structured data and create custom pack/unpack functions that turn the

entire record into std_logic_vector and back. To build up those

pack/unpacks, I use the following code; which I freely invite you or

anyone else to have at:

---

-------------------------------------------------------------------------------

-- Title : Useful Standard Functions Library

-------------------------------------------------------------------------------

-- File : standard_functions.vhd

-- Author : Rob Gaddi <rgaddi_at_highlandtechnology.com

-- Company : Highland Technology, Inc.

-- Created : 14-Feb-2012

-- Last update: 14-Feb-2012

-- Platform : Independent

-- Standard : VHDL 93 - 08

-------------------------------------------------------------------------------

-- Description: Useful standard functions.

-------------------------------------------------------------------------------

-- Revision History:

-------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;

use ieee.numeric_std.all;

package standard_functions is

-----------------------------------------------------------------------

-- Base 2 Logarithms

-----------------------------------------------------------------------

--! ceil(log2(x))

--! This is also the number of bits required to represent

--! x possible choices. clog2(4) = 2, clog2(5) = 3

pure function clog2(x : in positive) return natural;

--! floor(log2(x))

--! This is also the index (with the LSB as bit 0) of the

--! uppermost 1 bit for a given number represented in

--! binary. flog2(4) = 2, flog2(5) = 2

pure function flog2(x : in positive) return natural;

--! Number of bits needed to represent a given number in

--! binary form. nbits(4) = 3, nbits(5) = 3, nbits(-4) = 4

pure function nbits(x : in integer) return positive;

-----------------------------------------------------------------------

-- Conversion Functions

-----------------------------------------------------------------------

--! Converts an integer straight into a std_logic_vector.

pure function TO_SLV(data : in integer; bits : in positive) return

std_logic_vector;

--! Converts a logic value to std_logic using active-high rules.

pure function POS_LOGIC(x : in boolean) return std_logic;

--! Converts a logic value to std_logic using active-low rules.

pure function NEG_LOGIC(x : in boolean) return std_logic;

-----------------------------------------------------------------------

-- Record conversion functions

-----------------------------------------------------------------------

-- These are useful when having to turn arbitrary data collections

-- into std_logic_vector, generally for use with IP cores.

--

-- pack is used to line up multiple pieces of data in one

-- std_logic_vector; using a running variable that will reflect

-- the index of the LSB of the next data to write.

--

-- unpack is the reverse, pulling data off a vector.

--

-- These shouldn't be used to construct bitfields that will be

-- made accessible to the outside world; they're really just for

-- packing things up internally. Generally, you'll composite

-- these together to make larger functions that turn an entire

-- record type into and back from a std_logic_vector.

--

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in unsigned

);

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in signed

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic_vector

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out unsigned

);

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out signed

);

-----------------------------------------------------------------------

-- Comparison Functions

-----------------------------------------------------------------------

--! Performs a "counter comparison", in which only bits expected

--! to be '1' are actually checked. This is equivalent to a full

--! equality compare when used on an up-counter, but can take

--! substantially less logic because bits expected to be zero

--! aren't checked. For instance, to compare a counter to the

--! constant value 17 requires only 2 bits to be compared.

pure function counter_eq(x : in unsigned; constant tgt : in natural)

return boolean;

pure function counter_eq(x : in natural; constant tgt : in natural)

return boolean;

end package standard_functions;

package body standard_functions is

-----------------------------------------------------------------------

-- Base 2 Logarithms

-----------------------------------------------------------------------

pure function flog2 (x : in positive) return natural is

variable temp, log: natural;

begin

temp := x / 2;

log := 0;

while (temp /= 0) loop

temp := temp/2;

log := log + 1;

end loop;

return log;

end function flog2;

pure function clog2 (x : in positive) return natural is

begin

if (x = 1) then

return 1;

else

return 1 + flog2(x - 1);

end if;

end function clog2;

pure function nbits(x : in integer) return positive is

begin

if (x < 0) then

return 2 + flog2(-x);

elsif (x = 0) then

return 1;

else

return 1 + flog2(x);

end if;

end function nbits;

-----------------------------------------------------------------------

-- Conversion Functions

-----------------------------------------------------------------------

pure function TO_SLV( data : in integer;

bits : in positive)

return std_logic_vector is

begin

if (data < 0) then

return STD_LOGIC_VECTOR(TO_SIGNED(data, bits));

else

return STD_LOGIC_VECTOR(TO_UNSIGNED(data, bits));

end if;

end function TO_SLV;

pure function POS_LOGIC(x : in boolean) return std_logic is

begin

if x then

return '1';

else

return '0';

end if;

end function POS_LOGIC;

--! Converts a logic value to std_logic using active-low rules.

pure function NEG_LOGIC(x : in boolean) return std_logic is

begin

if x then

return '0';

else

return '1';

end if;

end function NEG_LOGIC;

-----------------------------------------------------------------------

-- Record conversion functions

-----------------------------------------------------------------------

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

) is

begin

target(idx + nd'length - 1 downto idx) := nd;

idx := idx + nd'length;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic

) is

begin

target(idx) := nd;

idx := idx + 1;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in unsigned

) is

begin

target(idx + nd'length - 1 downto idx) := STD_LOGIC_VECTOR(nd);

idx := idx + nd'length;

end procedure pack;

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in signed

) is

begin

target(idx + nd'length - 1 downto idx) := STD_LOGIC_VECTOR(nd);

idx := idx + nd'length;

end procedure pack;

----------------------------------------------------------------------

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic_vector

) is

begin

dat := source(idx + dat'length - 1 downto idx);

idx := idx + dat'length;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out std_logic

) is

begin

dat := source(idx);

idx := idx + 1;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out unsigned

) is

begin

dat := UNSIGNED(source(idx + dat'length - 1 downto idx));

idx := idx + dat'length;

end procedure unpack;

procedure unpack (

source : in std_logic_vector;

idx : inout integer;

dat : out signed

) is

begin

dat := SIGNED(source(idx + dat'length - 1 downto idx));

idx := idx + dat'length;

end procedure unpack;

-----------------------------------------------------------------------

-- Comparison Functions

-----------------------------------------------------------------------

pure function counter_eq(x : in unsigned; constant tgt : in natural)

return boolean is

variable target : unsigned(x'range);

begin

target := TO_UNSIGNED(tgt, target'length);

for i in target'range loop

if (target(i) = '1') and (x(i) /= '1') then

return false;

end if;

end loop;

return true;

end function counter_eq;

pure function counter_eq(x : in natural; constant tgt : in natural)

return boolean is

variable counter : unsigned(31 downto 0);

begin

counter := TO_UNSIGNED(x, 32);

return counter_eq(counter, tgt);

end function counter_eq;

end package body standard_functions;

--

Rob Gaddi, Highland Technology -- www.highlandtechnology.com

Email address domain is currently out of order. See above to fix.

Many thanks Rob, the functions look very useful. In fact, your 'pack' procedure appears to do exactly what I was originally wanting! It had not occurred to me to define the ports as inout though. The problem though is that the synthesis still infers a lot of logic to do this. I suspect the reason for this is that I need to make the procedure calls from within a process, in order for them to be executed sequentially. Is there another to achieve sequential execution without using a process block? My new code is below, using your 'pack' procedure. Maybe you can see some problem with this?

***********************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0) := (others => '0')

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

procedure pack(

target : inout std_logic_vector;

idx : inout integer;

nd : in std_logic_vector

) is

begin

target(idx + nd'length - 1 downto idx) := nd;

idx := idx + nd'length;

end procedure pack;

begin

BUS_ASSEMBLE : process

variable bus_tmp : std_logic_vector(5 downto 0) := (others => '0');

variable idx : integer := 0;

begin

bus_tmp := (others => '0');

pack (bus_tmp, idx, b);

pack (bus_tmp, idx, c);

pack (bus_tmp, idx, d);

bus_out(bus_tmp'length-1 downto 0) <= bus_tmp;

wait;

end process BUS_ASSEMBLE;

end rtl;

************************************************************

Your procedure only sets idx to 0 at the beginning of time, not at the

beginning of each execution. Then again, your wait waits for all of

time, so that's not helping you either.

Rather than write a process, you could write it as a function.

function pack_vectors(b, c, d: std_logic_vector) return std_logic_vector

variable bus_tmp : std_logic_vector(5 downto 0) := (others => '0');

variable idx : integer := 0;

begin

pack(bus_tmp, idx, b);

pack(bus_tmp, idx, c);

pack(bus_tmp, idx, d);

return bus_tmp;

end function pack_vectors;

...

bus_out <= pack_vectors(b, c, d);

But your process-based approach should work too.

BUS_ASSEMBLE : process(b, c, d)

variable bus_tmp : std_logic_vector(bus_out'range);

variable idx : integer;

begin

bus_tmp := (others => '0');

idx := 0;

pack (bus_tmp, idx, b);

pack (bus_tmp, idx, c);

pack (bus_tmp, idx, d);

bus_out <= bus_tmp;

end process BUS_ASSEMBLE;

--

Rob Gaddi, Highland Technology -- www.highlandtechnology.com

Email address domain is currently out of order. See above to fix.

Many thanks Rob, that's fantastic! So it turns out that the process in my original example would have worked too, had I correctly reset the index value on each iteration, as you pointed out. Still it's more readable, and looks a bit neater to use your procedure, especially if concatenating lots of signals. Cheers!

Guest

Fri Sep 14, 2018 5:45 am

On 2018-09-04 12:42, glenn.christian_at_gmail.com wrote:

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to und

erstand how to do this in the manner above.

Many thanks for your help,

Glenn.

I am trying to find a way in VHDL of assigning arbitrary length signals to a bus, and programmatically assigning the bus indices for these signals. A very simple example is given below, where I am manually assigning the three differing length signals to one bus:

*****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_out(0) <= b(0);

bus_out(1) <= b(1);

bus_out(2) <= b(2);

bus_out(3) <= c(0);

bus_out(4) <= c(1);

bus_out(5) <= d(0);

end rtl;

****************************************************************

This simulates and synthesises as expected, to a constantly driven "011101" output. Of course my real application is much more complicated than this, and involves assembling many more signals, of differing lengths, on to the bus, and I do not want to have to manually change all the indices, when the size of one of the signals changes. I would like to find a way to automatically provide the bus indices using, for example, the signal's 'length attributes, in a similar way to the following:

****************************************************************

library ieee;

use ieee.std_logic_1164.all;

entity bus_top is

port (

bus_out : out std_logic_vector(5 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(1 downto 0) := "11";

signal d : std_logic_vector(0 downto 0) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(bus_ind+b'length-1 downto bus_ind) <= b;

bus_ind := bus_ind + b'length;

bus_out(bus_ind+c'length-1 downto bus_ind) <= c;

bus_ind := bus_ind + c'length;

bus_out(bus_ind+d'length-1 downto bus_ind) <= d;

end process;

end rtl;

****************************************************************

Note the above code is not the correct solution as, although it appears to simulate correctly, the logic being sythesised is far more complex with many LUTs being inferred for what is just a static assignment. I feel it must be possible in VHDL to do this such that the synthesis tool will 'unroll' the statements in the same way as in the first example, eg for the indices to be calculated by the tools at compile-time rather than on the FPGA at run-time. This would require that the signals are assigned sequentially, and some way to keep track of the previous value of the bus index, but I have not been able to find the right construct for this: I have looked at processes (as above), regular loops, generate loops, functions and procedures. Can anyone shed some light on how to do this? I am aware there are other ways to achieve similar results, such as simple concatenation, or using a higher level programming language to automatically generate/annotate the VHDL, but I would like to und

erstand how to do this in the manner above.

Many thanks for your help,

Glenn.

It sure seems like we are trying to make this too hard. This looks like

a good application for a package.

How about something like this:

PACKAGE bus_lengths_pkg is

constant sigBlen : integer:=3;

constant sigClen : integer:=2;

constant sigDlen : integer:=1;

constant busoutlen : integer:=sigBlen+sigClen+sigDlen;

END bus_lengths_pkg;

library ieee;

use ieee.std_logic_1164.all;

use work.bus_lengths_pkg.all;

entity bus_top is

port (

bus_out : out std_logic_vector(busoutlen-1 downto 0)

);

end bus_top;

architecture rtl of bus_top is

signal b : std_logic_vector(busBlen-1 downto 0) := "101";

signal c : std_logic_vector(busClen-1 downto 0) := "11";

signal d : std_logic_vector(busDlen-1 downto 0) := "0";

begin

bus_out <= d & c & b;

end rtl;

Guest

Mon Sep 17, 2018 10:45 am

Thanks Charles,

I was aware I could use the concatenation operator (&), and simply calculate the required length of the resultant output bus, however I wanted a way to assign to arbitrary parts of the bus without necessarily considering it's total length, which might be fixed to something like 128 bits. The approach above with the combinatorial process pretty bus accomplishes this.

Best wishes, Glenn.

Guest

Wed Sep 19, 2018 10:45 pm

On Tuesday, September 4, 2018 at 1:42:19 PM UTC-4, glennchid wrote:

Hi,

I am trying to find a way in VHDL of assigning arbitrary length signals to a

bus, and programmatically assigning the bus indices for these signals. A

very simple example is given below, where I am manually assigning the three

differing length signals to one bus:

I am trying to find a way in VHDL of assigning arbitrary length signals to a

bus, and programmatically assigning the bus indices for these signals. A

very simple example is given below, where I am manually assigning the three

differing length signals to one bus:

What I do is to define a record and two functions that convert between that record and std_ulogic_vector. If you change the width of a field or add/subtract fields all that needs to be edited is the record definition and the to/from functions. There is no editing of anyplace where record elements are used, unless of course you've hard-coded in something such as 'bus.b <= "000";' and you change 'b' to be a four element vector instead of three..

If the overall bus length is flexible then you would have to be a little fancier inside the to_std_ulogic_vector function to define the return vector length.

This method synthesizes as expected, no actual logic is created when going through either the to_std_ulogic_vector or the from_std_ulogic_vector; to_std_ulogic_vector(from_std_ulogic_vector(x)) synthesizes to 'x'. Since the record definition and the functions are all generally together in the source code, you're only editing a handful of lines that are all right near each other.

Not sure if this helps your particular application, but I think it's at least close.

Here is an example

type t_CODE_FIFO_DATA is record

End_MCU: std_ulogic_vector(22 downto 22);

End_Rst_Interval: std_ulogic_vector(21 downto 21); -- Note: Not necessarily active at end of image

Code_Type: std_ulogic_vector(20 downto 20);

Consecutive_Zeros: std_ulogic_vector(19 downto 16);

Significant_Bits: std_ulogic_vector(15 downto 12);

Is_Zero: std_ulogic_vector(11 downto 11);

Magnitude: std_ulogic_vector(10 downto 0);

end record t_CODE_FIFO_DATA;

function To_Std_ULogic_Vector(L: t_CODE_FIFO_DATA) return std_ulogic_vector is

variable RetVal: std_ulogic_vector(22 downto 0);

begin

RetVal(L.End_MCU'range) := L.End_MCU;

RetVal(L.End_Rst_Interval'range) := L.End_Rst_Interval;

RetVal(L.Code_Type'range) := L.Code_Type;

RetVal(L.Consecutive_Zeros'range) := L.Consecutive_Zeros;

RetVal(L.Significant_Bits'range) := L.Significant_Bits;

RetVal(L.Is_Zero'range) := L.Is_Zero;

RetVal(L.Magnitude'range) := L.Magnitude;

return(RetVal);

end function To_Std_ULogic_Vector;

function From_Std_ULogic_Vector(L: std_ulogic_vector) return t_CODE_FIFO_DATA is

variable Lx: std_ulogic_vector(L'length - 1 downto 0);

variable RetVal: t_CODE_FIFO_DATA;

begin

Lx := L;

RetVal.End_MCU := Lx(RetVal.End_MCU'range);

RetVal.End_Rst_Interval := Lx(RetVal.End_Rst_Interval'range);

RetVal.Code_Type := Lx(RetVal.Code_Type'range);

RetVal.Consecutive_Zeros := Lx(RetVal.Consecutive_Zeros'range);

RetVal.Significant_Bits := Lx(RetVal.Significant_Bits'range);

RetVal.Is_Zero := Lx(RetVal.Is_Zero'range);

RetVal.Magnitude := Lx(RetVal.Magnitude'range);

return(RetVal);

end function From_Std_ULogic_Vector;

Kevin Jennings

Guest

Thu Sep 20, 2018 9:45 am

Like Rob and KJ I also like a record based approach, however, if you don't there are a couple of things you can do.

If you just wanted to brute force what you are doing it is:

bus_out <= d & c & b ;

If you assigned ranges that match where they go on the bus then you could make your code a little easier:

signal b : std_logic_vector(2 downto 0) := "101";

signal c : std_logic_vector(4 downto 3) := "11";

signal d : std_logic_vector(5 downto 5) := "0";

begin

-- Bus assembly ----

bus_assemble: process (b,c,d)

variable bus_ind : integer := 0;

begin

bus_out <= (others => '0'); -- avoids latch inference

bus_out(b'range) <= b;

bus_out(c'range) <= c;

bus_out(d'range) <= d;

end process;

end rtl;

elektroda.net NewsGroups Forum Index - VHDL Language - **automatic indexing for bus assembly in VHDL**