FPGA Central - World's 1st FPGA / CPLD Portal

FPGA Central

World's 1st FPGA Portal

 

Go Back   FPGA Groups > NewsGroup > VHDL

VHDL comp.lang.vhdl newsgroup / Usenet

Reply
 
LinkBack Thread Tools Display Modes
  #1 (permalink)  
Old 01-10-2009, 06:24 PM
Guest
 
Posts: n/a
Default Unassigned register decode

I have a question regarding unassigned values. I have a line of code,

fmt <= '1' when regA = "000" else '0';

Now if regA=XXX(unassigned) then I thought fmt will also be X or does
it depends on the simulator. I used vcs to try this and it shows
fmt=0.


Thanks,
Trescot

Reply With Quote
  #2 (permalink)  
Old 01-10-2009, 06:40 PM
Jonathan Bromley
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Sat, 10 Jan 2009 10:24:16 -0800 (PST), trescot wrote:

>I have a question regarding unassigned values. I have a line of code,
>
>fmt <= '1' when regA = "000" else '0';
>
>Now if regA=XXX(unassigned) then I thought fmt will also be X or does
>it depends on the simulator. I used vcs to try this and it shows
>fmt=0.


No, it doesn't depend on the simulator; VCS is doing exactly
what VHDL requires it to do. Equality comparison of
std_logic_vector, by default, does EXACT matching,
so if rega="XXX" then the comparison
regA = "000"
will be FALSE. Consequently you get '0' on the output,
exactly what you specified for the FALSE condition.

If you want to see X propagation in simulation, you
could do this:

fmt <= 'X' when is_X(regA)
else '1' when regA = "000"
else '0';

It sounds to me as though you come from a Verilog
background, and you are expecting the behaviour that
you would get from

assign fmt = (regA==3'b0) ? 1'b1 : 1'b0;

This will give fmt=1'bx when there are any X or Z
bits in regA. On the other hand, this:

always @(regA)
if (regA == 3'b0)
fmt = 1'b1;
else
fmt = 1'b0;

would behave in the same way as your VHDL code.

~~~~~~~~~~~~~~~~~~~~~~~~~ rant time ~~~~~~~~~~~~~~~

Better still, don't rely on X propagation to see
errors. Instead, catch the inappropriate use of Xs
using an assertion.

process (regA)
begin
assert not is_X(regA)
report "Bad value on regA"
severity ERROR;
-- Now we can ignore the possibility
-- that regA has unknowns, because the
-- assertion trapped that. So we can
-- write much simpler, cleaner code:
if to_X01(regA) = "000" then
fmt <= '1';
else
fmt <= '0';
end process;

Note the use of "to_X01" so that H and L bits
get strength-stripped to 1 and 0 respectively -
VERY important when writing simulation models
of things like I2C which use weak pullup or
pulldown on some signals.

In practice the to_X01 strength strip would
happen in just one place, where the input
signal comes out of its I/O buffer.

~~~~~~~~~~~~~~~~~ end of rant ~~~~~~~~~~~~~~~~~~
--
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
[email protected]
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #3 (permalink)  
Old 01-11-2009, 12:35 AM
Brian Drummond
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Sat, 10 Jan 2009 18:40:57 +0000, Jonathan Bromley
<[email protected]> wrote:

>On Sat, 10 Jan 2009 10:24:16 -0800 (PST), trescot wrote:
>
>>I have a question regarding unassigned values. I have a line of code,
>>
>>fmt <= '1' when regA = "000" else '0';
>>
>>Now if regA=XXX(unassigned) then I thought fmt will also be X or does
>>it depends on the simulator.

....
>Better still, don't rely on X propagation to see
>errors. Instead, catch the inappropriate use of Xs
>using an assertion.
>
> process (regA)
> begin
> assert not is_X(regA)
> report "Bad value on regA"
> severity ERROR;
> -- Now we can ignore the possibility
> -- that regA has unknowns, because the
> -- assertion trapped that. So we can
> -- write much simpler, cleaner code:
> if to_X01(regA) = "000" then
> fmt <= '1';
> else
> fmt <= '0';
> end process;


I like this approach though it is a little wordy for each use... it
seems to me you could embed all this functionality in, er, a function,
and write

fmt <= has_value(regA,"000");

- Brian

(actually writing the function is left as an exercise; but a pretty
straightforward one given the process above)
Reply With Quote
  #4 (permalink)  
Old 01-11-2009, 01:10 AM
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Jan 10, 7:35*pm, Brian Drummond <[email protected]>
wrote:
> On Sat, 10 Jan 2009 18:40:57 +0000, Jonathan Bromley
>
>
>
>
>
> <[email protected]> wrote:
> >On Sat, 10 Jan 2009 10:24:16 -0800 (PST), trescot wrote:

>
> >>I have a question regarding unassigned values. I have a line of code,

>
> >>fmt <= '1' when regA = "000" else '0';

>
> >>Now if regA=XXX(unassigned) then I thought fmt will also be X or does
> >>it depends on the simulator.

> ...
> >Better still, don't rely on X propagation to see
> >errors. *Instead, catch the inappropriate use of Xs
> >using an assertion.

>
> > *process (regA)
> > *begin
> > * *assert not is_X(regA)
> > * * *report "Bad value on regA"
> > * * *severity ERROR;
> > * *-- Now we can ignore the possibility
> > * *-- that regA has unknowns, because the
> > * *-- assertion trapped that. *So we can
> > * *-- write much simpler, cleaner code:
> > * *if to_X01(regA) = "000" then
> > * * *fmt <= '1';
> > * *else
> > * * *fmt <= '0';
> > *end process;

>
> I like this approach though it is a little wordy for each use... it
> seems to me you could embed all this functionality in, er, a function,
> and write
>
> fmt <= has_value(regA,"000");
>
> - Brian
>
> (actually writing the function is left as an exercise; but a pretty
> straightforward one given the process above)


std_match() ?

- Kenn
Reply With Quote
  #5 (permalink)  
Old 01-11-2009, 01:16 AM
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Jan 10, 8:10*pm, [email protected] wrote:
> On Jan 10, 7:35*pm, Brian Drummond <[email protected]>
> wrote:
>
>
>
> > On Sat, 10 Jan 2009 18:40:57 +0000, Jonathan Bromley

>
> > <[email protected]> wrote:
> > >On Sat, 10 Jan 2009 10:24:16 -0800 (PST), trescot wrote:

>
> > >>I have a question regarding unassigned values. I have a line of code,

>
> > >>fmt <= '1' when regA = "000" else '0';

>
> > >>Now if regA=XXX(unassigned) then I thought fmt will also be X or does
> > >>it depends on the simulator.

> > ...
> > >Better still, don't rely on X propagation to see
> > >errors. *Instead, catch the inappropriate use of Xs
> > >using an assertion.

>
> > > *process (regA)
> > > *begin
> > > * *assert not is_X(regA)
> > > * * *report "Bad value on regA"
> > > * * *severity ERROR;
> > > * *-- Now we can ignore the possibility
> > > * *-- that regA has unknowns, because the
> > > * *-- assertion trapped that. *So we can
> > > * *-- write much simpler, cleaner code:
> > > * *if to_X01(regA) = "000" then
> > > * * *fmt <= '1';
> > > * *else
> > > * * *fmt <= '0';
> > > *end process;

>
> > I like this approach though it is a little wordy for each use... it
> > seems to me you could embed all this functionality in, er, a function,
> > and write

>
> > fmt <= has_value(regA,"000");

>
> > - Brian

>
> > (actually writing the function is left as an exercise; but a pretty
> > straightforward one given the process above)

>
> std_match() ?
>
> *- Kenn


Hmmm. Scratch that. But being a fan of the *nix tool philosphy (many
small building blocks), I'd probably use some functions that made the
operations clear when reading the code:

fmt <= bool2slv( std_match(fail_if_X(regA), "000"));

where fail_if_X is the identity function but can thow an exception
(assert fails).

Of course, one man's clear is another VHDL 101 student's nightmare :-)

- Kenn
Reply With Quote
  #6 (permalink)  
Old 01-11-2009, 11:45 AM
Jonathan Bromley
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Sat, 10 Jan 2009 17:16:33 -0800 (PST), wrote:

> being a fan of the *nix tool philosphy (many
>small building blocks), I'd probably use some functions that made the
>operations clear when reading the code:
>
>fmt <= bool2slv( std_match(fail_if_X(regA), "000"));


Point taken, but there are times when a more integrated
approach pays dividends.

Beware - long-ish post follows - enter at your own risk.

I've struggled for years with the problem of how to
do register decode and readback in an elegant, flexible
way and at long last I think I have a reasonably nice
(partial, as ever) solution. Of course, it may be that
everyone else already has a better way of doing it, but
if so could they please speak up and tell us?

Here's what I have begun to do:

(1) Make a little package that contains just two things:
an enumeration that names all your registers (in any
order, doesn't matter) and a subtype declaration for the
data bus. This package will need to be changed as you
add new registers, keeping the enumeration list up to date.

package register_basics is
type T_reg_names is
( reg_widget
, reg_thingummy
, reg doodah
);
subtype T_reg_data is std_logic_vector(15 downto 0);
end package;

(2) Make a slightly bigger package that contains some
general-purpose stuff. This package will not change
as your set of registers grows and shrinks.

package register_infrastructure is
type T_reg_descriptor is record
adrs_offset: natural;
adrs_bits: natural;
end record;
type T_reg_map is array(T_reg_names) of T_reg_descriptor;
type T_reg_selects is array(T_reg_names) of std_logic;
type T_reg_readback is array(T_reg_names) of T_reg_data;
function match
( adrs: std_logic_vector
; reg: T_reg_descriptor
) return boolean;
endpackage

Type T_reg_descriptor is a record containing all I
need to know about each register: its offset address
relative to the base of the registers block, and the
number of address bits that are needed to select
individual words within it. We need that because
some peripheral designs have more than one register
in them, so some low-order address bits are used to
select individual registers within that block. For
simple registers containing only one word, that number
is set to zero. This data type could also include
information about whether the register is read-only,
and so forth.

Type T_reg_selects describes the set of register-select
lines (address decodes) that will activate each individual
register.

Type T_reg_readback describes the set of read-data buses,
one from each register, that will be centrally mux'd
together to form the whole register block's readback data.

function match() tells me whether a given incoming address
matches a particular register's descriptor, taking into
account the number of low-order address bits that need to
be ignored because they are used within the peripheral itself.
The match() function takes some thought, but it's no big deal.

(3) Now I can build a re-usable central register decode
and readback mux block.

entity reg_manager is
generic ( reg_map: T_reg_map );
port
( clock : in std_logic

...
-- strobe, timing, address, data to/from the bus master
...

-- Peripherals side
; reg_sel : out T_reg_selects -- one select per device
; reg_A : out std_logic_vector -- low-order addr to all
...
-- Strobes and write-data to all registers
...
; reg_rd_D : in T_reg_readback -- one read data per reg
);
end;

And the main clocked process body is amazingly simple:

if rising_edge(clock) then

-- timing, strobes etc...

-- Address decode and readback mux
--
reg_sel <= (others => '0');
reg_rd_D := (others => '0');
for p in reg_map'range loop
if match(reg_A, reg_map(p)) then
reg_sel(p) <= '1';
reg_rd_D := reg_rd_D or reg_rd_D(p);
end if;
end loop;
bus_data_out <= reg_rd_D;

As you can see, this just scans through the array
of register descriptors; when it finds one that matches,
it copies the corresponding readback data out to the bus
and drives the corresponding register-select line.

The top-level design needs these signals (amongst others):

signal reg_sel: T_reg_selects;
signal reg_rd_D: T_reg_readback;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ phew ~~~~~~~~~~~~~~~~~~~~~~~

Now we're ready to go. Please note that everything above,
with the exception of the very first package containing
the list-of-registers enumeration, is completely invariant
across different designs. Here's the payoff:

1) Instancing a peripheral couldn't be easier:

the_widget_register: entity work.widget
port map
( ...(
, select_me => reg_sel(reg_widget)
, my_read_data => reg_rd_D(reg_widget)
...
--- all other signals are common to all peripherals
...

2) You have just one instance of the reg_manager
entity, with a great big generic array on it.
It seems to me that this is a very clear way of
maintaining your register address map:

the_reg_manager: entity work.reg_manager
generic map ( reg_map =>
( reg_widget => (adrs_offset => 7, adrs_bits => 0)
, reg_thingummy => (adrs_offset => 5, adrs_bits => 0)
, reg_doodah => (adrs_offset => 10, adrs_bits => 1)
)
port map (...

Note that reg_doodah needs one address bit, and therefore
occupies addresses 10 and 11. The other registers have
no internal addressing and occupy just one address.

3) Bulletproofing is comparatively straightforward.
You can easily include quite elaborate assertion checks
to make sure that the reg_map generic is sensible (no
overlapping addresses, proper alignment of multi-word
peripherals, no out-of-bounds addresses, and so on).

Although the resulting model gives the simulator quite
a lot of work to do (linear search through the register
map for every access), in synthesis it builds really
nice logic.

A disadvantage: the register map information is in a
package, and therefore is global. VHDL-2008 package
generics could be used to fix this, but aren't yet
supported by any synth tool I know. Meanwhile, this
approach gives me the most flexible and clear way I've
yet discovered to create the register map of a medium-
sized design.
--
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
[email protected]
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #7 (permalink)  
Old 01-11-2009, 01:03 PM
Brian Drummond
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Sun, 11 Jan 2009 11:45:40 +0000, Jonathan Bromley
<[email protected]> wrote:

>Beware - long-ish post follows - enter at your own risk.
>
>I've struggled for years with the problem of how to
>do register decode and readback in an elegant, flexible
>way and at long last I think I have a reasonably nice
>(partial, as ever) solution.


Saved and printed,

Thanks!

- Brian
Reply With Quote
  #8 (permalink)  
Old 01-11-2009, 01:18 PM
Brian Drummond
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Sat, 10 Jan 2009 17:16:33 -0800 (PST), [email protected]
wrote:

>>
>> > fmt <= has_value(regA,"000");

>>
>> > - Brian

>>
>> > (actually writing the function is left as an exercise; but a pretty
>> > straightforward one given the process above)

>>
>> std_match() ?


>Hmmm. Scratch that. But being a fan of the *nix tool philosphy (many
>small building blocks), I'd probably use some functions that made the
>operations clear when reading the code:
>
>fmt <= bool2slv( std_match(fail_if_X(regA), "000"));
>
>where fail_if_X is the identity function but can thow an exception
>(assert fails).
>
>Of course, one man's clear is another VHDL 101 student's nightmare :-)


There are cases where I would agree with this approach; keeping all the
details transparent and open for inspection, when there is a real danger
of mistakes creeping in. (Rounding operations between fixed and floating
point, maybe, which are painfully easy to get subtly wrong!)

However for such a simple operation I disagree; reg_A either has_value
"000" or it doesn't, and you can reasonably expect the compiler to
complain if you get something like type conversion wrong.

Better (IMO) to reduce the cognitive load on the reader and let him
concentrate on more important (e.g. higher risk) aspects of the design.
There's a reason the Ada community talk about the "source program text"
.... clarity ... perish the thought of writing in code!

- Brian
Reply With Quote
  #9 (permalink)  
Old 01-11-2009, 05:35 PM
Mike Treseler
Guest
 
Posts: n/a
Default Re: Unassigned register decode

Jonathan Bromley wrote:

> Although the resulting model gives the simulator quite
> a lot of work to do (linear search through the register
> map for every access), in synthesis it builds really
> nice logic.


The Time Bandit strikes again

Thanks for the posting.
Much food for thought.

I'll bet modelsim is pretty good at linear search.
I can easily get a faster computer,
but I can't get a faster brain.

> Meanwhile, this
> approach gives me the most flexible and clear way I've
> yet discovered to create the register map of a medium-
> sized design.


I agree.
I like how you pull the static, database stuff out of time.

It might be possible to extend this idea
to infer the registers in the process that
consumes the data.

-- Mike Treseler
Reply With Quote
  #10 (permalink)  
Old 01-11-2009, 09:30 PM
Jonathan Bromley
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Sun, 11 Jan 2009 11:45:40 +0000, Jonathan Bromley wrote:

Sorry, a confusing typo crept in.

>(3) Now I can build a re-usable central register decode
>and readback mux block.
>
> entity reg_manager is
> generic ( reg_map: T_reg_map );
> port
> ( clock : in std_logic
>
> ...
> -- strobe, timing, address, data to/from the bus master
> ...
>
> -- Peripherals side
> ; reg_sel : out T_reg_selects -- one select per device
> ; reg_A : out std_logic_vector -- low-order addr to all
> ...
> -- Strobes and write-data to all registers
> ...
> ; reg_rd_D : in T_reg_readback -- one read data per reg
> );
> end;
>
>And the main clocked process body is amazingly simple:
>


It needs a variable declaration:

variable readback: T_reg_data;

> if rising_edge(clock) then
>
> -- timing, strobes etc...
>
> -- Address decode and readback mux
> --
> reg_sel <= (others => '0');
> reg_rd_D := (others => '0');


Nope, that should be
readback := (others => '0');

> for p in reg_map'range loop
> if match(reg_A, reg_map(p)) then
> reg_sel(p) <= '1';
> reg_rd_D := reg_rd_D or reg_rd_D(p);


And that should be
readback := readback or reg_rd_D(p);

> end if;
> end loop;
> bus_data_out <= reg_rd_D;


And finally:
bus_data_out <= readback;

Apologies. I transcribed this from a bit of code
that had some client-specific stuff in it, and it
lost something in the translation :-(
--
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
[email protected]
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #11 (permalink)  
Old 01-11-2009, 09:34 PM
Jonathan Bromley
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Sun, 11 Jan 2009 09:35:31 -0800, Mike Treseler wrote:

>I can easily get a faster computer,
>but I can't get a faster brain.


True, but I can assure you from experience that
it's easy to get a _slower_ brain: just wait
a couple of decades. See my recent correction
to the original post :-(
--
Jonathan Bromley, Autogerontologist

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

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
[email protected]
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #12 (permalink)  
Old 01-12-2009, 01:09 PM
KJ
Guest
 
Posts: n/a
Default Re: Unassigned register decode


"Jonathan Bromley" <[email protected]> wrote in message
news:[email protected]...
> On Sat, 10 Jan 2009 17:16:33 -0800 (PST), wrote:
> Of course, it may be that
> everyone else already has a better way of doing it, but
> if so could they please speak up and tell us?
>


'Better' is always in the eye of the beholder though

>
> Now we're ready to go. Please note that everything above,
> with the exception of the very first package containing
> the list-of-registers enumeration, is completely invariant
> across different designs.


And there is first bit of a rub from the standpoint of straight code re-use,
the package. If you keep the package, entity and architecture all together
and source control them, then each new design, since it will have a
different address map will need it's own package. But updating the source
file for the new design now breaks things for the old design which still
needs to be maintained. As the creator of such source, you'd want to
provide the 'package' in the form of commented out sample package code that
the user would need to create on their own. That way the code that is
invariant across all designs will not need any modification and could simply
be used.

The second rub which you're already aware of is that the package effectively
then
prevents you from using a second instance with different data widths in the
same
design. Most of the time this is not a problem since many designs simply
have
one processor type of bus so only one instance is needed, but it does
indicate where
the design doesn't scale. Maybe that's what you meant when you mentioned
something about using for a medium sized design.

>
> 1) Instancing a peripheral couldn't be easier:
>
> the_widget_register: entity work.widget
> port map
> ( ...(
> , select_me => reg_sel(reg_widget)
> , my_read_data => reg_rd_D(reg_widget)
> ...
> --- all other signals are common to all peripherals
> ...


Ummm....the reason one has read/write ports in a design in the first place
is because you need direct access to all of the bits in the ports at all
times. If you only need access to one port at any given time then what you
need is simple dual port memory. So you need an additional output that is
an array of T_reg_readback as an output of the entity as well.

>
> A disadvantage: the register map information is in a
> package, and therefore is global. VHDL-2008 package
> generics could be used to fix this, but aren't yet
> supported by any synth tool I know. Meanwhile, this
> approach gives me the most flexible and clear way I've
> yet discovered to create the register map of a medium-
> sized design.
> --


I use a two dimensional array of std_ulogic, that way data width and the
register list length are not limited by something in the package. The
package contains functions to convert to/from the 2d array to get a specific
register element. These functions are invariant across all designs. Within
each design there may also be an enumeration list of ports, which then
require conversion between the enumeration and an integer using 'val and
'pos attributes. Also, I don't actually implement the read/write ports in
that entity for a couple of reasons:

- Many designs have a bit or two in some port(s) that need to reset with
application of a reset signal. By implementing the ports themselves, you
either need to make a global decision to reset everything for every design
(which wastes routing resources, but some probably think is OK) or bring in
some generic parameters to specify which bits in which ports need to be
affected by reset and which do not (which makes the entity somewhat more
difficult to use). Neither problem is a *big* issue to deal with though.

- Sometimes the 'port select' is really a decode to some other external hunk
of code. In other words, the whole idea of implementing a global design
read/write port entity is not what you want in the first place. By simply
implementing the decoder/encoder to route data between the master and the
various slave devices (or ports) you get something that has no package
created restrictions and can scale as needed. For example, long lists of
registers running at a high clock speed will likely require multiple clocks
to do the muxing back to read data (which is going to be the critical timing
path element for this entity)...having a generic to control how many ticks
delay there is gives the user control of that function for dealing with
clock cycle performance of the module)

Lastly, a minor nit is the defaulting of the register read back data to '0'.
This is not needed and just adds additional routing and can slow down
performance in the (likely) critical path for muxing the data.

Thanks for the posting, nice to see higher level things than "my design
doesn't work...help"

Kevin Jennings


Reply With Quote
  #13 (permalink)  
Old 01-12-2009, 04:43 PM
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Jan 11, 6:45*am, Jonathan Bromley <[email protected]>
wrote:
> I've struggled for years with the problem of how to
> do register decode and readback in an elegant, flexible
> way and at long last I think I have a reasonably nice
> (partial, as ever) solution. *Of course, it may be that
> everyone else already has a better way of doing it, but
> if so could they please speak up and tell us?
>
> Here's what I have begun to do:


<much interesting text snipped>

I'll have to review your approach in more detail but it looks pretty
snazzy. I've taken a different approach to create banks of
registers. My desire was to move away from VHDL and towards a
specification template of sorts that is parsed by software and output
as both synthesizable VHDL and formatted documentation.

Example Input file

#-------------------------------------------------------------------------------
# Revision Register
#-------------------------------------------------------------------------------
NAME: REV
ADDR: 0x0
DIR: R
BITS: 7:0 Revision

#-------------------------------------------------------------------------------
# Control Register
#-------------------------------------------------------------------------------
NAME: CONTROL
ADDR: 0x0
DIR: W
SBIT: 2 StartSmStrobe
BIT: 1 AdcSmEn
BIT: 0 DacSmEn

#-------------------------------------------------------------------------------
# DAC Start Address Register
#-------------------------------------------------------------------------------
NAME: DACSTART
ADDR: 0x1
DIR: W
BITS: 11:0 DacStartAddr

etc...

The software groups the above registers together, determines the
maximum bus size required to read/write the largest register, creates
input and output ports for all signals and outputs a vhdl module that
you can instantiate in your code.

I need to add an optional comment field to be parsed and sent to my
documentation tools but the vhdl code generation works well enough to
be useful.

Pete
Reply With Quote
  #14 (permalink)  
Old 01-12-2009, 05:56 PM
Mike Treseler
Guest
 
Posts: n/a
Default Re: Unassigned register decode

[email protected] wrote:

> Example Input file

.. . .
> #-------------------------------------------------------------------------------
> # DAC Start Address Register
> #-------------------------------------------------------------------------------
> NAME: DACSTART
> ADDR: 0x1
> DIR: W
> BITS: 11:0 DacStartAddr
>
> etc...
>
> The software groups the above registers together, determines the
> maximum bus size required to read/write the largest register, creates
> input and output ports for all signals and outputs a vhdl module that
> you can instantiate in your code.


Interesting. It would seem that if an algorithm exists
to write synthesis code from that structure,
it ought to be possible to package a similar
vhdl record constant along with vhdl functions
and a looping synthesis process to do the same thing
at elaboration time.

Thanks for the posting.

-- Mike Treseler
Reply With Quote
  #15 (permalink)  
Old 01-12-2009, 07:18 PM
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Jan 12, 12:56*pm, Mike Treseler <[email protected]> wrote:
> Interesting. It would seem that if an algorithm exists
> to write synthesis code from that structure,
> it ought to be possible to package a similar
> vhdl record constant along with vhdl functions
> and a looping synthesis process to do the same thing
> at elaboration time.
>
> Thanks for the posting.
>
> * * * -- Mike Treseler- Hide quoted text -
>
> - Show quoted text -


The parser wasn't that difficult to write. It's just a TCL script.
The output vhdl is nicely formatted but obviously written by a
computer program. As long as the synthesis results were good, I
didn't worry too much about writing compact VHDL.

I've been trying to cut down the time it takes to generate VHDL code
for the more common functions that appear in my designs. This
includes state machines (vhdl and pdf diagrams) and testbench code. I
auto-generate from scripts as much as possible these days.

I've also been trying to get the documentation for code written
coincident with development. Documentation at the end of a project is
tedious and prone to error. Frankly, by the time the design is
complete I'm ready to move on to the next challenge. Unless required,
documentation tends to end up on the back burner indefinitely. I
figure if I can get a concise, specification template to spit out vhdl
*and* formatted documentation I'll be killing two birds with one
stone.
Reply With Quote
  #16 (permalink)  
Old 01-13-2009, 05:55 AM
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Jan 12, 8:09*am, "KJ" <[email protected]> wrote:
> "Jonathan Bromley" <[email protected]> wrote in message
>
> news:[email protected]...
>
> > On Sat, 10 Jan 2009 17:16:33 -0800 (PST), wrote:
> > Of course, it may be that
> > everyone else already has a better way of doing it, but
> > if so could they please speak up and tell us?

>
> 'Better' is always in the eye of the beholder though
>
>
>
> > Now we're ready to go. *Please note that everything above,
> > with the exception of the very first package containing
> > the list-of-registers enumeration, is completely invariant
> > across different designs.

>
> And there is first bit of a rub from the standpoint of straight code re-use,
> the package. *If you keep the package, entity and architecture all together
> and source control them, then each new design, since it will have a
> different address map will need it's own package. *But updating the source
> file for the new design now breaks things for the old design which still
> needs to be maintained. *As the creator of such source, you'd want to
> provide the 'package' in the form of commented out sample package code that
> the user would need to create on their own. *That way the code that is
> invariant across all designs will not need any modification and could simply
> be used.
>
> The second rub which you're already aware of is that the package effectively
> then
> prevents you from using a second instance with different data widths in the
> same
> design. *Most of the time this is not a problem since many designs simply
> have
> one processor type of bus so only one instance is needed, but it does
> indicate where
> the design doesn't scale. *Maybe that's what you meant when you mentioned
> something about using for a medium sized design.
>
>
>
> > 1) Instancing a peripheral couldn't be easier:

>
> > *the_widget_register: entity work.widget
> > *port map
> > * *( ...(
> > * *, select_me => reg_sel(reg_widget)
> > * *, my_read_data => reg_rd_D(reg_widget)
> > * *...
> > * *--- all other signals are common to all peripherals
> > * *...

>
> Ummm....the reason one has read/write ports in a design in the first place
> is because you need direct access to all of the bits in the ports at all
> times. *If you only need access to one port at any given time then whatyou
> need is simple dual port memory. *So you need an additional output thatis
> an array of T_reg_readback as an output of the entity as well.
>
>
>
> > A disadvantage: the register map information is in a
> > package, and therefore is global. *VHDL-2008 package
> > generics could be used to fix this, but aren't yet
> > supported by any synth tool I know. *Meanwhile, this
> > approach gives me the most flexible and clear way I've
> > yet discovered to create the register map of a medium-
> > sized design.
> > --

>
> I use a two dimensional array of std_ulogic, that way data width and the
> register list length are not limited by something in the package. *The
> package contains functions to convert to/from the 2d array to get a specific
> register element. *These functions are invariant across all designs. *Within
> each design there may also be an enumeration list of ports, which then
> require conversion between the enumeration and an integer using 'val and
> 'pos attributes. *Also, I don't actually implement the read/write portsin
> that entity for a couple of reasons:
>
> - Many designs have a bit or two in some port(s) that need to reset with
> application of a reset signal. *By implementing the ports themselves, you
> either need to make a global decision to reset everything for every design
> (which wastes routing resources, but some probably think is OK) or bring in
> some generic parameters to specify which bits in which ports need to be
> affected by reset and which do not (which makes the entity somewhat more
> difficult to use). *Neither problem is a *big* issue to deal with though.
>
> - Sometimes the 'port select' is really a decode to some other external hunk
> of code. *In other words, the whole idea of implementing a global design
> read/write port entity is not what you want in the first place. *By simply
> implementing the decoder/encoder to route data between the master and the
> various slave devices (or ports) you get something that has no package
> created restrictions and can scale as needed. *For example, long lists of
> registers running at a high clock speed will likely require multiple clocks
> to do the muxing back to read data (which is going to be the critical timing
> path element for this entity)...having a generic to control how many ticks
> delay there is gives the user control of that function for dealing with
> clock cycle performance of the module)
>
> Lastly, a minor nit is the defaulting of the register read back data to '0'.
> This is not needed and just adds additional routing and can slow down
> performance in the (likely) critical path for muxing the data.
>
> Thanks for the posting, nice to see higher level things than "my design
> doesn't work...help"
>
> Kevin Jennings


Thanks to both KJ and Jonathan for the good ideas. Agreed - some high-
level design is nice to see! I like some of the aspects that
Jonathan's approach brings. Having wrestled the register problem
myself, and in the spirit of really diving in, I'll throw the
following into the ring.

It seems to me that the main aspects his structure brings to the table
are

1) embedding of the register names into enum literals, adding to the
readability
2) a centralized and reusable decoder/mux through which all register
accesses are funnelled
3) a nice way of parameterizing and describing the registers to allow
(2) to be created

As has also been discussed, using a higher-level tool like Denali's
Blueprint, or other open source tools, can somewhat assist with point
(1) by automatically creating VHDL code which defines the constants
for you, (or with some code hacking, could probably build an enum).
I've seen an open-source tool in use (the name eludes me) which has a
nice HTML output so you can print out a nicely formatted 20 page
register spec as soon as your VHDL is compiled... it's very nice. You
can also get "C" language header files with all the right #define's to
make the software guys happy.

The centralized mux (item 2) along with the descriptors (3) are good
for describing *where* the registers are, but not so much for
describing *what* the registers are. Clearly, they will be domain-
specific, but may have some common characteristics.

In a lot of my designs, I have a clear delineation of register
classes: (a) simple read/write, like for static mode bits etc, (b)
read-only (like status and packet counters), (c) sticky alarm bits
with uniform "write-a-'1'-back-to-clear-it' semantics. I implemented
a framework which addresses aspect (3) above as well as the register
classifications. It's (yet again) partly orthogonal and partly
overlapping to both the above schemes.

I "crack" the incoming bus transaction into a single record type as
soon as the external bus hits the pins. There's always someone who
forgets whether CS is active H or L, and forgets whether or not got
gate WE with CS!

-- instead of separate cs, re, we flags we will use this
type TRANSACTION_KIND is ( T_IDLE, T_READ, T_WRITE);


-- this TYPE is used for interfaces from cracked external bus to
-- sub-blocks and direct registers. 'data' ignored for reads.
type TRANSACTION
is record
addr : std_logic_vector(ADDRWIDTH-1 downto 1);
data : std_logic_vector(BITS_PER_WORD - 1 downto 0);
action : TRANSACTION_KIND;
end record;

Then I set up a similar range descriptor, but used a bitmask to define
which bits are ignored when decoding the address.

type MEM_RANGE_DESCRIPTOR
is record
mask : std_logic_vector(ADDRWIDTH-1 downto 0);
match : std_logic_vector(ADDRWIDTH-1 downto 0);
end record;

Some simple predicates on transactions (is_in_range, is_read,
is_write, etc) make the later code fairly readable.

Now, if I want to build a simple read/write register I can use a
common procedure such as this:

-- ------------------------------------------------------------
--
-- procedure to create a regular read/write micro register
--
-- the register is a vector of D flops: loaded with value on write,
--
-- register is automatically decoded from address & bit index given
-- ------------------------------------------------------------
procedure micro_rw_reg (
constant reg_desc : in MEM_RANGE_DESCRIPTOR;
constant trans : in TRANSACTION;
signal reg : inout std_logic_vector(BITS_PER_WORD-1 downto 0)
)
is begin
if (is_in_range(trans, reg_desc)) and is_write(trans) then
reg <= trans.data;
end if;
end micro_rw_reg;

Or if I want to create only one bit of a register (say a register with
16 individual control bits) I can call the following procedure sixteen
times:

procedure micro_rw_bit (
constant reg_desc : in MEM_RANGE_DESCRIPTOR;
constant trans : in TRANSACTION;
signal reg : inout std_logic_vector(BITS_PER_WORD-1 downto 0);
constant index: in integer
)
is begin
if (is_in_range(trans, reg_desc)) and is_write(trans) then
reg(index) <= trans.data(index);
end if;
end micro_rw_bit;

Then the actual creation of, say, and interrupt source control
register could be built like this:

--
----------------------------------------------------------------------
--
-- irq source register
--
--
----------------------------------------------------------------------

src_reg_proc: process (clk)
begin
if rising_edge(clk) then
irq_src_reg(15 downto 2) <= (others => '0');
if reset = '1' then
irq_src_reg <= (others => '0');
else
micro_rw_bit( IRQ_SRC_REG_DESC, w_trans, irq_src_reg, 1);
micro_rw_bit( IRQ_SRC_REG_DESC, w_trans, irq_src_reg, 0);
end if;
end if; -- rising edge
end process src_reg_proc;

Where I had earlier defined a signal for irq_src_reg as well as a
descriptor:

constant IRQ_SRC_REG_DESC : MEM_RANGE_DESCRIPTOR :=
( mask => '0' & x"0FFFF", match => '0' & x"00018");

Wherease I could build the three word-wide foobar, fiddle, and faddle
registers by writing this:

constant FOOBAR_REG_DESC : MEM_RANGE_DESCRIPTOR :=
( mask => '0' & x"0FFFF", match => '0' & x"00000");

constant FIDDLE_REG_DESC : MEM_RANGE_DESCRIPTOR :=
( mask => '0' & x"0FFFF", match => '0' & x"00004");

constant FADDLE_REG_DESC : MEM_RANGE_DESCRIPTOR :=
( mask => '0' & x"0FFFF", match => '0' & x"00008");

reg_trinity_proc: process (clk)
begin
if rising_edge(clk) then
if reset = '1' then
foobar_reg <= (others => '0');
fiddle_reg <= (others => '0');
faddle_reg <= (others => '0');
else
micro_rw_reg( FOOBAR_REG_DESC, w_trans, foobar_reg);
micro_rw_reg( FIDDLE_REG_DESC, w_trans, fiddle_reg);
micro_rw_reg( FADDLE_REG_DESC, w_trans, faddle_reg);
end if;
end if; -- rising edge
end process reg_trinity_proc;

The approach to building the central decoder/read mux is similar to
Jonathan's but instead of decoding directly from the descriptors, I
decoded the main bus into fixed-size address segments (say on 64K
boundaries, which matches the mask of "ffff" above). Each fixed-size
segment gets routed to one "submodule" in the design; say one
submodule has a set of registers for an interrupt controller, one
submodule has registers for a video decoder, one submodule has the
chip common version, ID, and master reset registers, etc. The main
bus interface has N transaction output ports and N data readback
ports:

entity main_bus_interface
generic ( ADDRESS_CHUNK_SIZE_IN_BITS : integer ;
NPORTS : integer)
port ( ---
external bus pins
---
txn : TRANSACTION_VECTOR (0 to NPORTS); -- e.g. 64K per port
rdata : std_logic_vector(BITS_PER_WORD downto 0) );


I can build a subfunction (like a video processor, a packet interface,
or FIFO portal) in an entity which talks to the main_bus_interface
through a standard transaction bus. I can put down individual
registers fairly simply within each submodule using the procedures
described above. And furthermore, I can put down multiple subfunctions
(say three interrupt controllers and four video processors) by
instantiating multiple entities attached to the main_bus_interface;
each identical submodule then gets an identical set of registers
created at (for example) 64K offsets from each other by virtue of the
decoding performed in the main decoder. On the main decoder txn
ports, there would only be one port with a non-IDLE transaction
occurring at any given time.

So the clear drawbacks of this are:

- hard coding of ADDRWIDTH and BITS_PER_WORD - but they can be defined
in a package per FPGA
- lacking the elegance of identifying registers with enum literals
- the registers per se are not created automagically; they still have
to be written with a procedures
- pipelining and delay through the central entity have to be managed
somehow - but that's true always

But it kind of bridges the gap: I can simplify the act of defining a
register to some fairly well-named procedures, and force the designer
to declare some nice descriptors which help the VHDL documentation.
Plus I have a fairly resuable central bus entity.

My two bits.

- Kenn
Reply With Quote
  #17 (permalink)  
Old 01-13-2009, 12:03 PM
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Jan 13, 12:55*am, [email protected] wrote:
> On Jan 12, 8:09*am, "KJ" <[email protected]> wrote:
>
> > "Jonathan Bromley" <[email protected]> wrote in message

>
> >news:[email protected].. .

>
> > > On Sat, 10 Jan 2009 17:16:33 -0800 (PST), wrote:
> > > Of course, it may be that
> > > everyone else already has a better way of doing it, but
> > > if so could they please speak up and tell us?

>
> > 'Better' is always in the eye of the beholder though

>
> > > Now we're ready to go. *Please note that everything above,
> > > with the exception of the very first package containing
> > > the list-of-registers enumeration, is completely invariant
> > > across different designs.

>
> > And there is first bit of a rub from the standpoint of straight code re-use,
> > the package. *If you keep the package, entity and architecture all together
> > and source control them, then each new design, since it will have a
> > different address map will need it's own package. *But updating the source
> > file for the new design now breaks things for the old design which still
> > needs to be maintained. *As the creator of such source, you'd want to
> > provide the 'package' in the form of commented out sample package code that
> > the user would need to create on their own. *That way the code that is
> > invariant across all designs will not need any modification and could simply
> > be used.

>
> > The second rub which you're already aware of is that the package effectively
> > then
> > prevents you from using a second instance with different data widths inthe
> > same
> > design. *Most of the time this is not a problem since many designs simply
> > have
> > one processor type of bus so only one instance is needed, but it does
> > indicate where
> > the design doesn't scale. *Maybe that's what you meant when you mentioned
> > something about using for a medium sized design.

>
> > > 1) Instancing a peripheral couldn't be easier:

>
> > > *the_widget_register: entity work.widget
> > > *port map
> > > * *( ...(
> > > * *, select_me => reg_sel(reg_widget)
> > > * *, my_read_data => reg_rd_D(reg_widget)
> > > * *...
> > > * *--- all other signals are common to all peripherals
> > > * *...

>
> > Ummm....the reason one has read/write ports in a design in the first place
> > is because you need direct access to all of the bits in the ports at all
> > times. *If you only need access to one port at any given time then what you
> > need is simple dual port memory. *So you need an additional output that is
> > an array of T_reg_readback as an output of the entity as well.

>
> > > A disadvantage: the register map information is in a
> > > package, and therefore is global. *VHDL-2008 package
> > > generics could be used to fix this, but aren't yet
> > > supported by any synth tool I know. *Meanwhile, this
> > > approach gives me the most flexible and clear way I've
> > > yet discovered to create the register map of a medium-
> > > sized design.
> > > --

>
> > I use a two dimensional array of std_ulogic, that way data width and the
> > register list length are not limited by something in the package. *The
> > package contains functions to convert to/from the 2d array to get a specific
> > register element. *These functions are invariant across all designs. *Within
> > each design there may also be an enumeration list of ports, which then
> > require conversion between the enumeration and an integer using 'val and
> > 'pos attributes. *Also, I don't actually implement the read/write ports in
> > that entity for a couple of reasons:

>
> > - Many designs have a bit or two in some port(s) that need to reset with
> > application of a reset signal. *By implementing the ports themselves,you
> > either need to make a global decision to reset everything for every design
> > (which wastes routing resources, but some probably think is OK) or bring in
> > some generic parameters to specify which bits in which ports need to be
> > affected by reset and which do not (which makes the entity somewhat more
> > difficult to use). *Neither problem is a *big* issue to deal with though.

>
> > - Sometimes the 'port select' is really a decode to some other externalhunk
> > of code. *In other words, the whole idea of implementing a global design
> > read/write port entity is not what you want in the first place. *By simply
> > implementing the decoder/encoder to route data between the master and the
> > various slave devices (or ports) you get something that has no package
> > created restrictions and can scale as needed. *For example, long lists of
> > registers running at a high clock speed will likely require multiple clocks
> > to do the muxing back to read data (which is going to be the critical timing
> > path element for this entity)...having a generic to control how many ticks
> > delay there is gives the user control of that function for dealing with
> > clock cycle performance of the module)

>
> > Lastly, a minor nit is the defaulting of the register read back data to'0'.
> > This is not needed and just adds additional routing and can slow down
> > performance in the (likely) critical path for muxing the data.

>
> > Thanks for the posting, nice to see higher level things than "my design
> > doesn't work...help"

>
> > Kevin Jennings

>
> Thanks to both KJ and Jonathan for the good ideas. Agreed - some high-
> level design is nice to see! I like some of the aspects that
> Jonathan's approach brings. Having wrestled the register problem
> myself, and in the spirit of really diving in, I'll throw the
> following into the ring.
>
> It seems to me that the main aspects his structure brings to the table
> are
>
> 1) embedding of the register names into enum literals, adding to the
> readability
> 2) a centralized and reusable decoder/mux through which all register
> accesses are funnelled
> 3) a nice way of parameterizing and describing the registers to allow
> (2) to be created
>
> As has also been discussed, using a higher-level tool like Denali's
> Blueprint, or other open source tools, can somewhat assist with point
> (1) by automatically creating VHDL code which defines the constants
> for you, (or with some code hacking, could probably build an enum).
> I've seen an open-source tool in use (the name eludes me) which has a
> nice HTML output so you can print out a nicely formatted 20 page
> register spec as soon as your VHDL is compiled... it's very nice. You
> can also get "C" language header files with all the right #define's to
> make the software guys happy.
>
> The centralized mux (item 2) along with the descriptors (3) are good
> for describing *where* the registers are, but not so much for
> describing *what* the registers are. *Clearly, they will be domain-
> specific, but may have some common characteristics.
>
> In a lot of my designs, I have a clear delineation of register
> classes: (a) simple read/write, like for static mode bits etc, (b)
> read-only (like status and packet counters), (c) sticky alarm bits
> with uniform "write-a-'1'-back-to-clear-it' semantics. *I implemented
> a framework which addresses aspect (3) above as well as the register
> classifications. It's (yet again) partly orthogonal and partly
> overlapping to both the above schemes.
>
> I "crack" the incoming bus transaction into a single record type as
> soon as the external bus hits the pins. There's always someone who
> forgets whether CS is active H or L, and forgets whether or not got
> gate WE with CS!
>
> * -- instead of separate cs, re, we flags we will use this
> * type TRANSACTION_KIND is ( T_IDLE, T_READ, T_WRITE);
>
> * -- this TYPE is used for interfaces from cracked external bus to
> * -- sub-blocks and direct registers. 'data' ignored for reads.
> * type TRANSACTION
> * * is record
> * * * * *addr : std_logic_vector(ADDRWIDTH-1 downto 1);
> * * * * *data : std_logic_vector(BITS_PER_WORD - 1 downto 0);
> * * * * *action : TRANSACTION_KIND;
> * * * *end record;
>
> Then I set up a similar range descriptor, but used a bitmask to define
> which bits are ignored when decoding the address.
>
> * type MEM_RANGE_DESCRIPTOR
> * is record
> * * * *mask *: std_logic_vector(ADDRWIDTH-1 downto 0);
> * * * *match : std_logic_vector(ADDRWIDTH-1 downto 0);
> * * *end record;
>
> Some simple predicates on transactions (is_in_range, is_read,
> is_write, etc) make the later code fairly readable.
>
> Now, if I want to build a simple read/write register I can use a
> common procedure such as this:
>
> * -- ------------------------------------------------------------
> * --
> * -- procedure to create a regular read/write micro register
> * --
> * -- the register is a vector of D flops: loaded with value on write,
> * --
> * -- register is automatically decoded from address & bit index given
> * -- ------------------------------------------------------------
> * procedure micro_rw_reg (
> * * constant reg_desc : in MEM_RANGE_DESCRIPTOR;
> * * constant trans : in TRANSACTION;
> * * signal * reg *: inout std_logic_vector(BITS_PER_WORD-1 downto0)
> * * )
> * is begin
> * * * if (is_in_range(trans, reg_desc)) and is_write(trans) then
> * * * * reg <= trans.data;
> * * * end if;
> * end micro_rw_reg;
>
> Or if I want to create only one bit of a register (say a register with
> 16 individual control bits) I can call the following procedure sixteen
> times:
>
> * procedure micro_rw_bit (
> * * constant reg_desc : in MEM_RANGE_DESCRIPTOR;
> * * constant trans : in TRANSACTION;
> * * signal * reg *: inout std_logic_vector(BITS_PER_WORD-1 downto0);
> * * constant index: in integer
> * * )
> * is begin
> * * * if (is_in_range(trans, reg_desc)) and is_write(trans) then
> * * * * reg(index) <= trans.data(index);
> * * * end if;
> * end micro_rw_bit;
>
> Then the actual creation of, say, and interrupt source control
> register could be built like this:
>
> * --
> ----------------------------------------------------------------------
> * --
> * -- irq source register
> * --
> * --
> ----------------------------------------------------------------------
>
> * src_reg_proc: process (clk)
> * begin
> * * if rising_edge(clk) then
> * * * irq_src_reg(15 downto 2) <= (others => '0');
> * * * if reset = '1' then
> * * * * irq_src_reg <= (others => '0');
> * * * else
> * * * * micro_rw_bit( IRQ_SRC_REG_DESC, w_trans, irq_src_reg, 1);
> * * * * micro_rw_bit( IRQ_SRC_REG_DESC, w_trans, irq_src_reg, 0);
> * * * end if;
> * * end if; * * * * * * * * * * * * * * -- rising edge
> * end process src_reg_proc;
>
> Where I had earlier defined a signal for irq_src_reg as well as a
> descriptor:
>
> * constant IRQ_SRC_REG_DESC : MEM_RANGE_DESCRIPTOR :=
> * * ( mask => '0' & x"0FFFF", match => '0' & x"00018");
>
> Wherease I could build the three word-wide foobar, fiddle, and faddle
> registers by writing this:
>
> * constant FOOBAR_REG_DESC : MEM_RANGE_DESCRIPTOR :=
> * * ( mask => '0' & x"0FFFF", match => '0' & x"00000");
>
> * constant FIDDLE_REG_DESC : MEM_RANGE_DESCRIPTOR :=
> * * ( mask => '0' & x"0FFFF", match => '0' & x"00004");
>
> * constant FADDLE_REG_DESC : MEM_RANGE_DESCRIPTOR :=
> * * ( mask => '0' & x"0FFFF", match => '0' & x"00008");
>
> * reg_trinity_proc: process (clk)
> * begin
> * * if rising_edge(clk) then
> * * * if reset = '1' then
> * * * * foobar_reg <= (others => '0');
> * * * * fiddle_reg <= (others => '0');
> * * * * faddle_reg <= (others => '0');
> * * * else
> * * * * micro_rw_reg( FOOBAR_REG_DESC, w_trans, foobar_reg);
> * * * * micro_rw_reg( FIDDLE_REG_DESC, w_trans, fiddle_reg);
> * * * * micro_rw_reg( FADDLE_REG_DESC, w_trans, faddle_reg);
> * * * end if;
> * * end if; * * * * * * * * * * * * * * -- rising edge
> * end process reg_trinity_proc;
>
> The approach to building the central decoder/read mux is similar to
> Jonathan's but instead of decoding directly from the descriptors, I
> decoded the main bus into fixed-size address segments (say on 64K
> boundaries, which matches the mask of "ffff" above). Each fixed-size
> segment gets routed to one "submodule" in the design; say one
> submodule has a set of registers for an interrupt controller, one
> submodule has registers for a video decoder, one submodule has the
> chip common version, ID, *and master reset registers, etc. The main
> bus interface has N transaction output ports and N data readback
> ports:
>
> entity main_bus_interface
> generic ( ADDRESS_CHUNK_SIZE_IN_BITS : integer ;
> * * * * * NPORTS : integer)
> port ( ---
> * * * *external bus pins
> * * * *---
> * * * *txn : TRANSACTION_VECTOR (0 to NPORTS); -- e.g. 64K per port
> * * * *rdata : std_logic_vector(BITS_PER_WORD downto 0) );
>


Oops.. rdata should be a VECTOR 0 to NPORTS of words, not a single
word, where each word is an slv(BITS_PER_WORD downto 0).

> I can build a subfunction (like a video processor, a packet interface,
> or FIFO portal) in an entity which talks to the main_bus_interface
> through a standard transaction bus. I can put down individual
> registers fairly simply within each submodule using the procedures
> described above. And furthermore, I can put down multiple subfunctions
> (say three interrupt controllers and four video processors) by
> instantiating multiple entities attached to the main_bus_interface;
> each identical submodule then gets an identical set of registers
> created at (for example) 64K offsets from each other by virtue of the
> decoding performed in the main decoder. *On the main decoder txn
> ports, there would only be one port with a non-IDLE transaction
> occurring at any given time.
>
> So the clear drawbacks of this are:
>
> - hard coding of ADDRWIDTH and BITS_PER_WORD - but they can be defined
> in a package per FPGA
> - lacking the elegance of identifying registers with enum literals
> - the registers per se are not created automagically; they still have
> to be written with a procedures
> - pipelining and delay through the central entity have to be managed
> somehow - but that's true always
>
> But it kind of bridges the gap: I can simplify the act of defining a
> register to some fairly well-named procedures, and force the designer
> to declare some nice descriptors which help the VHDL documentation.
> Plus I have a fairly resuable central bus entity.
>
> My two bits.
>
> *- Kenn


Reply With Quote
  #18 (permalink)  
Old 01-13-2009, 08:23 PM
Jim Lewis
Guest
 
Posts: n/a
Default Re: Unassigned register decode

Brian
>>> snip ...
>>>
>>> fmt <= '1' when regA = "000" else '0';
>>>
>>> Now if regA=XXX(unassigned) then I thought fmt will also be X or does
>>> it depends on the simulator.

> ...
>
> I like this approach though it is a little wordy for each use... it
> seems to me you could embed all this functionality in, er, a function,
> and write
>
> fmt <= has_value(regA,"000");
>
> - Brian
>
> (actually writing the function is left as an exercise; but a pretty
> straightforward one given the process above)


This is done for you in VHDL-2008 via the new relation operators
that are preceded by a "?"

fmt <= regA ?= "000" ;

Returns 1 if equal, 0 if not equal, X if contains an X.

Cheers,
Jim

P.S.
This came into the language with Accellera VHDL-2006 and
is now part of IEEE approved standard 1076-2008, so kick
your vendor if they have neglected to implement it.

--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~
Jim Lewis SynthWorks VHDL Training http://www.synthworks.com

A bird in the hand may be worth two in the bush,
but it sure makes it hard to type.
Reply With Quote
  #19 (permalink)  
Old 01-13-2009, 08:47 PM
Jim Lewis
Guest
 
Posts: n/a
Default Re: Unassigned register decode

KJ,
> Lastly, a minor nit is the defaulting of the register read back data to '0'.
> This is not needed and just adds additional routing and can slow down
> performance in the (likely) critical path for muxing the data.


I do this in all of my blocks / cores that are integrated into a chip.
This makes your top level mux an "OR" gate. Should you have a performance
problem, an OR at the top level should be easier to restructure than
a multiplexer with an address going to it.

Cheers,
Jim
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~
Jim Lewis SynthWorks VHDL Training http://www.synthworks.com

A bird in the hand may be worth two in the bush,
but it sure makes it hard to type.
Reply With Quote
  #20 (permalink)  
Old 01-13-2009, 10:12 PM
Jonathan Bromley
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Mon, 12 Jan 2009 08:09:50 -0500, "KJ" wrote:

>And there is first bit of a rub from the standpoint of straight code re-use,
>the package. If you keep the package, entity and architecture all together
>and source control them, then each new design, since it will have a
>different address map will need it's own package. But updating the source
>file for the new design now breaks things for the old design which still
>needs to be maintained.


Unquestionably true. I see the code I offered more as a "design
pattern" than a re-usable block, at least until we have
package generics.

>The second rub [...] Maybe that's what you meant when you mentioned
>something about using for a medium sized design.


Yes, exactly. It scales OK for one class of problem
(registers only on one specific bus in a given design)
but doesn't scale to bigger problems than that.
Believe me, I would love to find a fix :-)

>Ummm....the reason one has read/write ports in a design in the first place
>is because you need direct access to all of the bits in the ports at all
>times. If you only need access to one port at any given time then what you
>need is simple dual port memory. So you need an additional output that is
>an array of T_reg_readback as an output of the entity as well.


I think you missed the point here. Each register gets
implemented as an entity instance. The physical output
bits of the register find their way on to ordinary
individual signals - or, perhaps, into the guts of
a peripheral block. My common decoder block doesn't
implement the registers' storage at all; my experience
is that this storage is likely to be too diverse to
centralise.

> I don't actually implement the read/write ports in
>that entity for a couple of reasons:


Yes, we're on the same page here.

>Lastly, a minor nit is the defaulting of the register read back data to '0'.
>This is not needed


Yes it is needed. The code I presented is an AND-OR mux,
and doesn't work without the zero default. I am perfectly
well aware that there are other ways of doing it, but the
way I presented gives pretty efficient mux logic from all
the synth tools I've tried, and it is clean and easy to
understand, so that's good enough for me.

>Thanks for the posting, nice to see higher level things


Thanks. I really meant it when I said that I had struggled
with this for some time, and that there are probably other
ways; I appreciate all the comments.
--
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
[email protected]
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #21 (permalink)  
Old 01-13-2009, 10:59 PM
Jonathan Bromley
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Mon, 12 Jan 2009 21:55:24 -0800 (PST), wrote:

[snip Good Stuff]

>- the registers per se are not created automagically; they still have
>to be written with a procedures


True for my effort too. I didn't make it clear enough
in my first post that the registers themselves live in
individual entities, or procedures, or whatever. I was
only describing the decode and readback mux arrangements.
My arrays-indexed-by-enum make it rather easy to hook up
the "bus" side of each of the register entities, but they
still need to be instantiated.

>- pipelining and delay through the central entity have to be managed
>somehow - but that's true always


Sure. And there is bus-bridge functionality to be managed; the
common internal "register bus" in your FPGA/ASIC is unlikely to
be as complicated as the external bus structure that needs
decoding.

>But it kind of bridges the gap: I can simplify the act of defining a
>register to some fairly well-named procedures, and force the designer
>to declare some nice descriptors which help the VHDL documentation.
>Plus I have a fairly resuable central bus entity.


More stuff for me to think about. Thanks.
--
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
[email protected]
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #22 (permalink)  
Old 01-13-2009, 11:59 PM
Jonathan Bromley
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Mon, 12 Jan 2009 11:18:26 -0800 (PST), Petrov_101 wrote:

>The parser wasn't that difficult to write. It's just a TCL script.


It's worth noting that you can sometimes get Tcl to *be* the
parser for you. Hierarchically-structured things like
register definitions can map nicely on to a Tcl *script*:

reg_bank -base 0x3456 -name Test_reg {
register -name Control_reg -offset 0x00 {
reg_field -bits 15 -mode read_only -name topBit
# add more fields here
}
register -name Data_reg -offset 0x01 {
reg_field -name data -bits 15:8
reg_field -name error -bits 1:0
reg_field -name status -bits {7:5 3}
}
}

Now, by defining appropriate Tcl procs for
[reg_bank], [register] and [reg_field] you can
make your Tcl script generate VHDL code, documentation,
and perhaps even some testbench code.

Explanation: procedure [reg_bank] has three arguments:
the option "-name", the option value "0x3456", and the
script in curly brackets containing two [register] commands.
And so on. I imagine the "-bits" option to reg_field
working like this: it's a Tcl list each of whose elements
is a bit-range; these bit-ranges can either be a single
bit number, or a range like 15:0

ACKNOWLEDGEMENT: Idea shamelessly stolen^wleveraged from
the "RAL" package that forms part of the VMM SystemVerilog
verification methodology toolkit.

EXAMPLE - very incomplete - Tcl code:
Source the following code into any Tcl interpreter,
then source the register-definition script (above)
and see the result...
Lots of error-checking and other details missing.

proc reg_bank {args} {
# last argument is the register description script
set script [lindex $args end]
# pick up option-value pairs:
foreach {option value} [lrange $args 0 end-1] {
switch -- $option {
-base {set ::reg_base $value}
-name {set ::base_name $value}
default {error "Unknown option $option"}
}
}
# display info about this register bank
puts "Processing register bank $::base_name at address $::reg_base"
# execute the script in the calling context
uplevel 1 $script
}

proc register {args} {
set script [lindex $args end]
foreach {option value} [lrange $args 0 end-1] {
switch -- $option {
-name {set ::reg_name $value}
-offset {set ::reg_adrs [expr {$::reg_base + $value}]}
default {error "Unknown option $option"}
}
}
puts [format " Processing register %s/%s at address 0x%08x" \
$::base_name $::reg_name $::reg_adrs]
uplevel 1 $script
}

proc reg_field {args} {
set name "unnamed"
set mode "read_write"
foreach {option value} $args {
switch -- $option {
-name {set field_name $value}
-bits {set bits_list $value}
-mode {set mode $value}
default {error "Unknown option $option"}
}
}
# Make a bits-string from the "bits" option:
set bitmap [split [string repeat . 16] {}]
foreach bit_spec $bits_list {
# Is there a : in it??
if {[regexp {^\d+$} $bit_spec]} {
set bit_spec $bit_spec:$bit_spec
}
if {[regexp {^(\d+)\d+)$} $bit_spec -> hi lo]} {
for {set i $lo} {$i <= $hi} {incr i} {
lset bitmap end-$i X
}
} else {
error "Bit-spec must be N or hi:lo"
}
}
set bitmap [join $bitmap {}]
puts [format " field %-10s: %s %s" $field_name $bitmap $mode]
}


--
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
[email protected]
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
Reply With Quote
  #23 (permalink)  
Old 01-14-2009, 03:24 AM
KJ
Guest
 
Posts: n/a
Default Re: Unassigned register decode


"Jim Lewis" <[email protected]> wrote in message
news:[email protected] treetonline...
> KJ,
>> Lastly, a minor nit is the defaulting of the register read back data to
>> '0'. This is not needed and just adds additional routing and can slow
>> down performance in the (likely) critical path for muxing the data.

>
> I do this in all of my blocks / cores that are integrated into a chip.
> This makes your top level mux an "OR" gate. Should you have a performance
> problem, an OR at the top level should be easier to restructure than
> a multiplexer with an address going to it.
>


My only point here was that a straight mux will be the minimal logic/routing
resource usage solution. Adding the default to zero if nothing is being
addressed (or any other solution really) will use at least as much or more
resources as compared to the straight mux, it will not use less than the mux
unless that other solution is able to map into some special hardware
resource of some kind in the device. If there is no such special beast, and
this data path is in the critical timing path, you might find that zeroing
the data bus when an unsupported address is being indexed is a luxury that
is not worth the price...whether or not it is a problem in a given design
depends on how many ports are addressable and the logic block resources of
the FPGA that is implementing them...like I said, it's a minor nit,
something to be aware of when the situation arises.

Kevin Jennings


Reply With Quote
  #24 (permalink)  
Old 01-14-2009, 01:29 PM
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On Jan 13, 6:59*pm, Jonathan Bromley <[email protected]>
wrote:
> On Mon, 12 Jan 2009 11:18:26 -0800 (PST), Petrov_101 wrote:
> >The parser wasn't that difficult to write. *It's just a TCL script.

>
> It's worth noting that you can sometimes get Tcl to *be* the
> parser for you. *Hierarchically-structured things like
> register definitions can map nicely on to a Tcl *script*:


<snip>

Quite true... one of the reasons I like the language so much. I
designed a VHDL processor years ago and needed an assembler for it. I
defined each mnemonic as a tcl procedure. My make file would concat
the mnemonic file with my assembly probram and run it through the tcl
shell. The output was a xilinx block ram preloaded with machine
instructions.
Reply With Quote
  #25 (permalink)  
Old 01-14-2009, 02:08 PM
Andreas Ehliar
Guest
 
Posts: n/a
Default Re: Unassigned register decode

On 2009-01-14, KJ <[email protected]> wrote:
> I've only seen the and-or take the same or more, never seen it take
> less. I'd be interested in seeing an example of one where it took
> less resources with the and-or then with the mux to perform the same
> function.



I see the same results in a quick test where a mux like structure was
compared with an or-type structure). Registers were used on both the
inputs and the outputs (and the design was coded so that the reset
inputs of the input flip-flops should be used).

When optimized for speed, the area of both solutions were very similar
(within a few percent) but when synthesized for area, the or-based
structure was 8 percent larger than the plain mux.

(This is for an ASIC and not an FPGA. Other tradeoffs can sometimes
apply in an FPGA, especially if you need those registers on the inputs
anyway. And it might also be slightly different in another ASIC process
than the one I tested this on. And the synthesis tool could do something
different in a real design and not a synthetic test, etc etc. You probably
shouldn't base company critical decisions on this very quick analysis )

/Andreas
Reply With Quote
Reply

Bookmarks


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


Similar Threads
Thread Thread Starter Forum Replies Last Post
decode this code. dipesh.trivedi Verilog 2 06-19-2006 02:50 AM
How to decode FAR register in Virtex-4? Bertrand Rousseau FPGA 6 02-14-2006 03:06 PM
Unassigned pins Jaroslaw Pawelczyk FPGA 2 01-17-2006 02:23 PM
[ISE7.1] Equivalent register removal + register duplication + register balancing Tim Verstraete FPGA 0 12-08-2005 03:37 PM
Dolby Digital AC-3 Decode on an FPGA - Possible ? Big ? PeterC FPGA 2 09-28-2005 05:43 PM


All times are GMT +1. The time now is 11:54 AM.


Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.2.0
Copyright 2008 @ FPGA Central. All rights reserved