Accessing records in VHDL

Status
Not open for further replies.

shaiko

Advanced Member level 5
Joined
Aug 20, 2011
Messages
2,644
Helped
303
Reputation
608
Reaction score
297
Trophy points
1,363
Activity points
18,302
Hello,

I created this record:
Code:
type type_variable_size_word is
record
  one_byte_word : std_logic_vector ( 1 * 8 - 1 downto 0 ) ;
  two_byte_word : std_logic_vector ( 2 * 8 - 1 downto 0 ) ;
  three_byte_word : std_logic_vector ( 3 * 8 - 1 downto 0 ) ;
  four_byte_word : std_logic_vector ( 4 * 8 - 1 downto 0 ) ;
end record ;

signal variable_size_word  : type_variable_size_word ;
I also have these 3 signals:
Code:
signal a : std_logic_vector ( 1 * 8 - 1 downto 0 ) ;
signal b : std_logic_vector ( 2 * 8 - 1 downto 0 ) ;
signal c : std_logic_vector ( 3 * 8 - 1 downto 0 ) ;
signal d : std_logic_vector ( 4 * 8 - 1 downto 0 ) ;
What is the syntax to assign a , b , c and d accordingly to my record signal ?
 

What is the syntax to assign a , b , c and d accordingly to my record signal ?
Code:
signal variable_size_word:  type_variable_size_word;

-- One way
variable_size_word.one_byte_word  <= a;
variable_size_word.two_byte_word <= b;
variable_size_word.three_byte_word <= c;
variable_size_word.four_byte_word  <= d;

-- Another way
variable_size_word <=
(
    one_byte_word  => a,
    two_byte_word => b,
    three_byte_word => c,
    four_byte_word  => d
);

Kevin Jennings
 
Thanks Kevin.
This brings me to my second question - suppose I have a very long record:
Code:
one_byte_word : std_logic_vector ( 1 * 8 - 1 downto 0 ) ;
two_byte_word : std_logic_vector ( 2 * 8 - 1 downto 0 ) ;
.
.
.
hundred_byte_word : std_logic_vector ( 100 * 8 - 1 downto 0 ) ;
Is there some way to do the assignments in an iterative fashion (like a generate loop) ?
 

Is there some way to do the assignments in an iterative fashion (like a generate loop) ?
No, not with record elements. However, if you only need only one of the elements and you just don't know ahead of time which one that would be, then you could use a generate loop but there you would define the vector size as a function of the generate index.

Kevin
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
And what about iterative record declaration?
Something like:
Code:
type type_variable_size_word is
record
  iterative_record_declaration : for index in 1 to 100 
  generate
      x_byte_word : std_logic_vector ( index * 8 - 1 downto 0 ) ;
  end generate ;
end record ;
I'm sure the above isn't valid - but is there any VHDL mechanism that can enable such abstraction ?
 

You're right, it is not valid but it's not clear to me just what abstraction you're trying to get at anyway, you'll need to clarify. Some questions that will help:
- Do you really need to access all elements of a record with the 100 elements of std_logic_vectors or is there some more limited scope that needs to be accessed?
- Does the base element need to be std_logic_vector or can it be a scalar such as an integer?

Some things that can be easily done:
- Generate 100 different sized vectors where the size of the std_logic_vector is defined by a generate index. Within the scope of the generate statement, one can then use the std_logic_vector, but outside of that scope that signal would not be visible. Is that OK?
- An array of integers or an array of a record. The base record would have two elements: the number itself and the size that you would like it to be in bits when cast as a std_logic_vector

Examples that demonstrate these two:
Code:
Ex1 : for index in 1 to 100 generate
    signal x_byte_word : std_logic_vector ( index * 8 - 1 downto 0 ) ;
begin
    ....
    -- Here you can access only the 'x_byte_word' that corresponds with the single value
    -- of 'index'.  There would be no visibility to the 'x_byte_word' that goes along with any 
    -- other value of index.
end generate Ex1;

type t_Ex2 is record
    Value: integer; -- Will limit you though to the size of a VHDL integer, 31 bits
    Sizeof: positive;
end record t_Ex2;
type arr_t_Ex2 is array(natural range<>) of t_Ex2;
signal My_Sig:  arr_t_Ex2(1 to 100);
-- Now you have complete access to any of the 100 values.  They can be cast into
-- std_logic_vectors like this...

some_slv <= std_logic_vector(to_unsigned(My_Sig(i).Value, My_Sig(i).Sizeof));

Kevin Jennings
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
This is the real scenario I'm facing:

Altera's Avalon bus - the data width is 128 bits ( 16 symbols of 8 bits each )
The "empty" signal must be evaluated with every transaction ( not just when EOP is received ) and the data bus must be sliced accordingly.

Therefore, I want to have a set of 16 vectors (same as the number of symbols). The incoming data will than be sliced using to the empty signal and fed to the appropriately sized vector.

I want to do it generically...Is it possible to declare a set of 16 vectors and make each one to be of a different size?
Can your Ex1 do it?
 

Why are you slicing the data bus? the empty signal is parralel information you can use to generate register enables.
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
Why are you slicing the data bus?
Because I'm using the incoming 128 bit chunks to assemble a much bigger message of 12,800 bits.

For example:

First cycle:
empty is 0
So we take data bits 127 downto 0 and drive it to bits 127 downto 0 of the bigger message register.

Second cycle:
empty is 2
So we take data bits 111 downto 0 and drive it to bits 239 downto 128 of the bigger message register.

And so on...
 
Last edited:

Still sounds like you want register enables. The 128 data bits go to all 100 sets. You enable bits 127 downto 0 when empty is 0, 239 downto 128 when empty is 1, etc. Each bank of 128 clocks in the data only when its particular enable bit is set to 1 in which case it clocks in all 128 bits. There is no need to do anything with the data. Just create a 100 bit vector of register enable signals and bring the 128 data bits into all 100 destinations of the bigger message register.

Kevin
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
Like this:
Code:
number_of_valid_symbols <= number_of_symbols - empty ;

generate_enable_vector : for index in 1 to number_of_symbols 
generate
  if number_of_valid_symbols >= index then 
    enable_vector ( index ) <= '1' ;
  else
    enable_vector ( index ) <= '0' ;
  end if ;	
end generate generate_enable_vector ;
?
 

This line looks questionable...

if number_of_valid_symbols >= index then

Since it will select multiple bits of 'enable_vector' and set them to '1'. Maybe that's what you want, I dunno. I would've expected an = not >=

Kevin
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
vhdl2008 allows unconstrained vectors in records. eg:
Code:
record DataBus is
  data : std_logic_vector;
  en : std_logic;
end record;

https://www.doulos.com/knowhow/vhdl_designers_guide/vhdl_2008/vhdl_200x_small/

just remember that this will have similar issues as other unconstrained vectors. In this case, you can't assign "open" to an unused output as the type dimensions will not be known and could be used within the entity as an attribute. I'm not sure if you can assign to a vector without declaring it, but unsigned vectors as inputs also have issues if you assume the direction is "downto", or if you assume 0 is a valid index.
 

Kevin,

This line looks questionable...

if number_of_valid_symbols >= index then

Since it will select multiple bits of 'enable_vector' and set them to '1'. Maybe that's what you want, I dunno. I would've expected an = not >=

Perhaps I didn't explain it properly.

Incoming data is 128 bits ( 8 bits per symbol = 16 symbols ).

Example:

First cycle:
empty is 15 ( only one valid symbol )
So we take data bits 7 downto 0 and drive it to bits 7 downto 0 of the bigger message register.

Second cycle:
empty is 14 ( only two valid symbols )
So we take data bits 15 downto 0 and drive it to bits 23 downto 8 of the bigger message register.
We do so, because the previous cycle filled only bits (7 downto 0). In other words - I want to discard the empty message section and "squeeze" the new valid data to the exact place where the last transaction finished.

Only when empty = 128 our pointer makes a full 128 bit increment.

Something in this spirit...only synthesizable:
Code:
number_of_valid_symbols <= number_of_symbols - empty ;

process ( clock ) is
begin 
   if rising_edge ( clock ) then
      if valid = '1' then
         whole_message ( ( pointer + number_of_valid_symbols * symbol_size - 1 ) downto pointer <= incoming_message ( number_of_valid_symbols * symbol_size - 1 downto 0 ) ;
         pointer <= pointer + number_of_valid_symbols * symbol_size ;
      end if ;
   end if ;	
end process ;
Can you post an example that does that?
 
Last edited:

The logic for this I forsee being rather hiddeous. Why do you need to remove the invalid words?
Please think about how you would draw this circuit (because you did document the design before you wrote the code right?) before writing the code.

I see this as more a design/architecture issue rather than coding problem. Why are you using empty on a per word basis? what is the data format?
 

The logic for this I forsee being rather hiddeous
I know it would...

Why do you need to remove the invalid words?
Because the whole message will have to be parsed in parallel after I'm done filling it.
If I don't do it in this stage - I'll just have to take care of it in the next level.

Why are you using empty on a per word basis?
I'm getting the Avalon Stream from an third party IP - and this is how it works.

Please think about how you would draw this circuit (because you did document the design before you wrote the code right?)
The only way I can visualize it is with pre-slicing the data bus to all the possible vector sizes...
 

The only way I can visualize it is with pre-slicing the data bus to all the possible vector sizes...

Thats not visualising it, thats just thinking about how to code for it.
Im assuming you're buffering the word for a PC? Does it go into some MM interface? probably just easier let the controller remove the padding.
 

Im assuming you're buffering the word for a PC
No. The application is entirely embedded in the FPGA - no software involvement.

- - - Updated - - -

BTW:
What VHDL rules does this violate?
Code:
current_valid_symbols <= total_symbols - empty ;

process ( clock ) is
begin 
  if rising_edge ( clock ) then
    if valid = '1' and current_valid_symbols  > 1 then
      big_message ( ( pointer + current_valid_symbols * 8 - 1 ) downto pointer ) <= message ( current_valid_symbols  * 8 - 1 downto 0 ) ;
      pointer <= pointer + current_valid_symbols * 8 ;
    end if ;
  end if ;	
end process ;
 

It doesnt violate any VHDL rules, assuming pointer and current_valid_symbols are integers.
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating

I think the basic thing here is that you need to keep track of where to put the next incoming message, but still the data does not need to be muxed around, it just needs to be selectively enabled. Here is a starter of how I would approach it

Code:
constant MAX_SYMBOLS: natural := 16;
signal Next_Symbol: natural range 0 to (MAX_SYMBOLS-1); -- The range of symbols
signal empty:  natural range 0 to (MAX_SYMBOLS-1); -- From Avalon
constant BITS_PER_SYMBOL: natural := 8;
signal Data_In:  std_ulogic_vector((BITS_PER_SYMBOL-1) downto 0); -- From Avalon
constant MSGS_PER_BIG_MSG: natural := 100; -- What you had stated
signal My_Big_Msg:  std_ulogic_vector(BITS_PER_SYMBOL*MAX_SYMBOLS*MSGS_PER_BIG_MSG-1 downto 0);
...
process(clock)
begin
    if rising_edge(clock) then
        if (Some_Form_Of_Reset = '1') then
            Next_Symbol <= 0;
        elsif (valid = '1') then
            [COLOR=#ff0000]if (TBD) then -- How to handle wrapping needs to be decided[/COLOR]
                Next_Symbol <= 0;
            else
                Next_Symbol <= Next_Symbol + MAX_SYMBOLS-1-empty;
            end if;
        end if;

        for i in 0 to (MSGS_PER_BIG_MSG-1) loop
            if (Next_Symbol >= i) then -- KJ:  Essentially creates the enable signal here
                for i in Data_In'range loop
                    My_Big_Message(BITS_PER_SYMBOL * i) <= Data_In(i);
                end loop;
           end if;
        end loop;
    end if;
end process;
First, the above code is most likely wrong and is only meant to get across the ideas which are:
- Keep track of where to put the next incoming data (That is the 'Next_Symbol' calculations)
- Store the incoming data in the appropriate location (That is the nested loops for 'My_Big_Message')

Lastly, while this code is synthesizable, it is just meant to be an outline of how one could go about it. I don't know all of the details of your design, the thing that is being interfaced with or anything like that. I don't know what is supposed to happen if the incoming data spills over and splits more than one of your bigger messages (i.e. if you're one symbol short of having the full 100 but you get more than one input symbol).

Kevin Jennings
 
Reactions: shaiko

    shaiko

    Points: 2
    Helpful Answer Positive Rating
Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…