enum as array index

S

Shannon

I think this is how I have to do it. Am I making it too complex?
Will it synthesize?


type flag_type is (this, that, other);
type flag_array_type is array (flag_type range <>) of std_logic;
signal flag : flag_array_type(other downto this);

....
--set the flag
flag(that) <= '1';

yes?

Shannon
 
M

Mike Treseler

Shannon said:
type flag_type is (this, that, other);
type flag_array_type is array (flag_type range <>) of std_logic;
signal flag : flag_array_type(other downto this);

I use the type enum directly as the array index:

type quality_t is (too_big, too_small, just_right);

type stats_t is array (quality_t) of natural;

constant stats_c : stats_t :=
(too_small => 2, too_big => 1, just_right => 5);

Works fine for synthesis.

-- Mike Treseler
 
S

Shannon

I use the type enum directly as the array index:

type quality_t is (too_big, too_small, just_right);

type stats_t is array (quality_t) of natural;

constant stats_c : stats_t :=
(too_small => 2, too_big => 1, just_right => 5);

Works fine for synthesis.

    -- Mike Treseler

I was specific about the ordering since I am going to use the flag
array to update a vector where the specific location of each bit is
important (and fixed). Something like:

Loop
reg_status(i) <= reg_status(i) or flag(i);
end loop

This flag array is a list of fault bits.

I like the looks of:

flag(over_current) <= '1';
....
flag(under_volt) <= '1';
etc.
Then the processor can read the reg_status word and can mask bits to
look for certain faults. So the plan was to enum in order the fault
flags.

Shannon
 
S

Shannon

Loop
   reg_status(i) <= reg_status(i) or flag(i);
end loop

Which of course I just realized I can't do since the index here would
be an integer and wouldn't match flag_type. grrrr

Shannon
 
S

Shannon

In that case, you really DON'T want to use an enum.
It gives you no control over the mapping from
enum positions to bit positions in the vector.

Consider named constants instead.  That gives you
the same readable usage, but anchors the flag
positions in the std_logic_vector.  It also
allows you to create named bit-fields by using
subtypes.

As an example, suppose you have an 8-bit
flag vector laid out like this:

  bit 7: flag "f7"
  bits 6..5: two-bit control code "cc2"
  bit 4:  flag "f4"
  bits 3..1: three-bit control code "cc3"
  bit 0: flag "f0"

Then you could do...

  subtype flag_vector_t is std_logic_vector (7 downto 0);
  constant f7: positive := 7;
  subtype cc2 is positive range 6 downto 5;
  subtype code2_t is std_logic_vector(cc2);
  constant f4: positive := 4;
  subtype cc3 is positive range 3 downto 1;
  subtype code3_t is std_logic_vector(cc3);
  constant f0: positive := 0;

  signal flags: flag_vector_t;
  signal code3: code3_t;
  signal code2: code2_t;

  ...

  flags(f0) <= '1';
  flags(cc2) <= code2;
  code3 <= flags(cc3);
  if flags(f0)='1' then ...

But of course your flag word is now simply a
std_logic_vector, which can freely be copied
to or from any other such.

There are other games you can play with records,
but named constants and subtypes are flexible
and painless.  They don't bring you a whole lot
of type checking, but there are times when you
don't want that anyway.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
(e-mail address removed)://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.

Thanks Jonathan. This is the reason I posted since I had a sneaking
suspicion I was making things too hard. You are right of course. I
would make the "look-up table" more readable anyway:

constant over_current : positive 1;
constant over_voltage : positive 2;
etc...
rather than having to count your way through and enum (which could get
reordered by the synth anyway...)

Thanks.
 
M

Mike Treseler

Shannon said:
Which of course I just realized I can't do since the index here would
be an integer and wouldn't match flag_type. grrrr

Shannon


hmmm.
'pos worked with quartus like this:
____________________________________________________________________
function op2vec (arg : op_t)
return std_logic_vector is
variable argn_v : natural;
-----------------------------------------
-- type op_t is (ts_load, cal_load, cmd2, cmd3);
-- 00 01 10 11 -- cal_adr port values
-----------------------------------------
begin
argn_v := op_t'pos(arg);
return std_logic_vector(to_unsigned(argn_v, adr_len_c));
end function op2vec;
____________________________________________________________________

-- Mike Treseler
 
K

KJ

Shannon said:
I was specific about the ordering since I am going to use the flag
array to update a vector where the specific location of each bit is
important (and fixed). Something like:

Loop
reg_status(i) <= reg_status(i) or flag(i);
end loop

This flag array is a list of fault bits.

For processor register maps I prefer to define a record type like this...
type t_INPUT_DMA_STATUS_PORT is record
Done: std_ulogic_vector(31 downto 31);
Reserved: std_ulogic_vector(30 downto 26);
Current_Count: std_ulogic_vector(25 downto 0);
end record;

Then create a pair of functions to convert to and from std_ulogic_vectors
like this...
function To_Std_ULogic_Vector(L : t_INPUT_DMA_STATUS_PORT) return
std_ulogic_vector is
variable RetVal: std_ulogic_vector(31 downto 0);
begin
RetVal := (others => '0');
RetVal(L.Done'range) := L.Done;
RetVal(L.Reserved'range) := L.Reserved;
RetVal(L.Current_Count'range) := L.Current_Count;
return(RetVal);
end To_Std_ULogic_Vector;

function From_Std_ULogic_Vector(L : std_ulogic_vector) return
t_INPUT_DMA_STATUS_PORT is
variable RetVal: t_INPUT_DMA_STATUS_PORT;
variable Lx: std_ulogic_vector(L'length - 1 downto 0);
begin
Lx := L;
RetVal.Done := Lx(RetVal.Done'range);
RetVal.Reserved := Lx(RetVal.Reserved'range);
RetVal.Current_Count := Lx(RetVal.Current_Count'range);
return(RetVal);
end From_Std_ULogic_Vector;

All of this goes into a package that goes along with the entity.

Creating and maintaining these functions as the basic type evolves over time
is fairly straight forward, in fact changes that involves only the bits
within an existing field (like making a particular field be two bits wide
rather than one) involve editing only the type definition to put in the new
bit definitions, no other edits are needed, just recompile. Adding or
removing fields only requires editing the record and the to/from functions.
If there is any code anywhere else in the model that then would be affected
(such as X.Done <= "1" where the 'Done' field for whatever reason now
changes to be 2 bits) you don't need to hunt these affected areas down,
simply recompile and the compiler will flag it for you (in this case the
complaint being the assignment of a vector of size one to something that is
two bits wide)...much less tedious.

Having done that bit of groundwork (which can be somewhat or mostly
'macro-ized' depending on your fav editor's capabilities) lots of things
come out looking clean

Simple assignment:
Status.Done <= "1";
Logic operations (such as masking the bits of one reg with another) are
handled mostly by type conversion using the to/from functions:
Status <= From_Std_ULogic_Vector(To_Std_ULogic_Vector(Status) and
To_Std_ULogic_Vector(Mask_Reg));

When you get around to writing the testbench code for the processor model
these to/from functions will come in handy yet again since the model for the
'source code' of the processor will look clean dealing with the record types
(as above for 'simple assignment') but at some point the basic 'processor
model' itself will need to communicate via generic std_(u)logic_vectors.
That I generally wrap up inside a procedure (or function) in process scope
like this...

procedure Write_Port(Data: t_INPUT_DMA_STATUS_PORT)

Do that for each and every software port in your design and the testbench
processor model reduces down to a whole bunch of simple 'write_port' and
'read_port' calls without having to even specify addresses of those ports in
the call since those procedures can be written to contain the address by
virtue of VHDL's function/procedure overloading. As long as you only have
one port that is of type 't_INPUT_DMA_STATUS_PORT' the address of the port
can be defined as a constant in the procedure, so no need to pass it in as a
parameter as you would with a less general 'write anything' or 'read
anything' set of procedures. These 'register specific' read and write
procedures in turn generally call a more basic 'read' or 'write' procedure
that works with std_(u)logic_vectors that implements whatever the basic
protocol of a read or write actually is at the signal level (i.e. set strobe
low, wait 100 ns, drop 'wr_n'...blah, blah...) and works with any address
and data since it is working at the std_(u)logic_vector level.

Kevin Jennings
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top