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

FPGA Central

World's 1st FPGA Portal

 

Go Back   FPGA Groups > NewsGroup > FPGA

FPGA comp.arch.fpga newsgroup (usenet)

Reply
 
LinkBack Thread Tools Display Modes
  #1 (permalink)  
Old 03-21-2007, 12:07 AM
Guest
 
Posts: n/a
Default Virtex-II block RAM problem

Hello,

I am running into a strange problem with the dual-port block RAM in
Virtex-II and at this point have run out of ideas

In my design I am trying to perform data acquisition on a 32-bit parallel
data stream running at 125 MHz. I have two memory blocks into which I want
to be able to direct the data: internal block RAM and external SRAM.
Internal RAM is dual ported with one port connected to the local bus. SRAM
controller has can be switched between the local bus and the data port.
Data source is the same for both block RAM and the SRAM - their inputs are
taken from a single signal. Within the design I have a little test pattern
generator to produce a fake data stream for testing.

So here is the problem: the data is written correctly into the SRAM, but
not into the block RAM. It is a timing problem - errors go away if I lower
the clock frequency.

The same problem persist with both ISE 8.2i and 9.1i. Here are some
numbers:

The part is XC2V3000-4FF1152. Clock constraint is 7.6 ns. Static timing
analysis gives me 7.632 ns. Experimentally-determined maximum clock
frequency for error-free acquisition into the block RAM is 105 MHz.
Maximum clock frequency for SRAM - around 150 MHz.

As I play around with block RAM (instantiated vs. inferred, pipelining in
front of the memory), maximum frequency moves in the range from 90 to
120 MHz. SRAM maximum frequency is consistently around 150 MHz. Block RAM
errors are typically single-bit, sometimes two bits. Which bit it is seems
to move around from one compilation to another.

--
Dmitry Teytelman
Reply With Quote
  #2 (permalink)  
Old 03-21-2007, 01:50 AM
Peter Alfke
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

On Mar 20, 4:07 pm, [email protected] wrote:
> Hello,
>
> I am running into a strange problem with the dual-port block RAM in
> Virtex-II and at this point have run out of ideas
>
> In my design I am trying to perform data acquisition on a 32-bit parallel
> data stream running at 125 MHz. I have two memory blocks into which I want
> to be able to direct the data: internal block RAM and external SRAM.
> Internal RAM is dual ported with one port connected to the local bus. SRAM
> controller has can be switched between the local bus and the data port.
> Data source is the same for both block RAM and the SRAM - their inputs are
> taken from a single signal. Within the design I have a little test pattern
> generator to produce a fake data stream for testing.
>
> So here is the problem: the data is written correctly into the SRAM, but
> not into the block RAM. It is a timing problem - errors go away if I lower
> the clock frequency.
>
> The same problem persist with both ISE 8.2i and 9.1i. Here are some
> numbers:
>
> The part is XC2V3000-4FF1152. Clock constraint is 7.6 ns. Static timing
> analysis gives me 7.632 ns. Experimentally-determined maximum clock
> frequency for error-free acquisition into the block RAM is 105 MHz.
> Maximum clock frequency for SRAM - around 150 MHz.
>
> As I play around with block RAM (instantiated vs. inferred, pipelining in
> front of the memory), maximum frequency moves in the range from 90 to
> 120 MHz. SRAM maximum frequency is consistently around 150 MHz. Block RAM
> errors are typically single-bit, sometimes two bits. Which bit it is seems
> to move around from one compilation to another.
>
> --
> Dmitry Teytelman



Reply With Quote
  #3 (permalink)  
Old 03-21-2007, 01:56 AM
Peter Alfke
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Dmitry, whatever timing poblems you have has nothing to do with the
BlockRAM itself.
It is a synchronous device (think of it as a flip-flop or register)
with a data and address input set-up time below 1 ns, and no hold time
requirement. Clock-to-out (for reading) can be up to 3 ns.
If you have problems below 150 MHz, those timing problems are
elsewhere.
I hope you use a global clock for clocking the BRAM and the adjacent
logic...
Peter Alfke, Xilinx Applications

On Mar 20, 4:07 pm, [email protected] wrote:
> Hello,
>
> I am running into a strange problem with the dual-port block RAM in
> Virtex-II and at this point have run out of ideas
>
> In my design I am trying to perform data acquisition on a 32-bit parallel
> data stream running at 125 MHz. I have two memory blocks into which I want
> to be able to direct the data: internal block RAM and external SRAM.
> Internal RAM is dual ported with one port connected to the local bus. SRAM
> controller has can be switched between the local bus and the data port.
> Data source is the same for both block RAM and the SRAM - their inputs are
> taken from a single signal. Within the design I have a little test pattern
> generator to produce a fake data stream for testing.
>
> So here is the problem: the data is written correctly into the SRAM, but
> not into the block RAM. It is a timing problem - errors go away if I lower
> the clock frequency.
>
> The same problem persist with both ISE 8.2i and 9.1i. Here are some
> numbers:
>
> The part is XC2V3000-4FF1152. Clock constraint is 7.6 ns. Static timing
> analysis gives me 7.632 ns. Experimentally-determined maximum clock
> frequency for error-free acquisition into the block RAM is 105 MHz.
> Maximum clock frequency for SRAM - around 150 MHz.
>
> As I play around with block RAM (instantiated vs. inferred, pipelining in
> front of the memory), maximum frequency moves in the range from 90 to
> 120 MHz. SRAM maximum frequency is consistently around 150 MHz. Block RAM
> errors are typically single-bit, sometimes two bits. Which bit it is seems
> to move around from one compilation to another.
>
> --
> Dmitry Teytelman



Reply With Quote
  #4 (permalink)  
Old 03-21-2007, 02:35 AM
Duane Clark
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

[email protected] wrote:
> ...
> So here is the problem: the data is written correctly into the SRAM, but
> not into the block RAM. It is a timing problem - errors go away if I lower
> the clock frequency.
> ...


Show the actual clock constraints you are using.
Reply With Quote
  #5 (permalink)  
Old 03-21-2007, 06:13 AM
Dmitry Teytelman
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

On Tue, 20 Mar 2007, Peter Alfke wrote:

> Dmitry, whatever timing poblems you have has nothing to do with the
> BlockRAM itself.
> It is a synchronous device (think of it as a flip-flop or register)
> with a data and address input set-up time below 1 ns, and no hold time
> requirement. Clock-to-out (for reading) can be up to 3 ns.


In the past I've had no problems using the blockRAM for similar tasks, so
these problems really came as a surprise. In fact, the code has evolved
from the older version (which never had such issues). I am trying now to
redo the transition step-by-step to see where the problems start.

Unfortunately, at this point my trust in tools is not too great,
especially since XST will not properly synthesize dual-port blockRAM coded
according to the templates distributed with the ISE (examples_v9.zip).

> I hope you use a global clock for clocking the BRAM and the adjacent
> logic...


The data acquisition logic as well as BRAMs are clocked by a DCM which
drives a BUFG. Here is the timing constraint for the DCM input clock:

NET "clkp" TNM_NET = FFS(*) "clkp";
TIMESPEC "TS_clkp" = PERIOD "clkp" 3.8 ns HIGH 50 %;

--
Dmitry Teytelman
Reply With Quote
  #6 (permalink)  
Old 03-21-2007, 06:18 AM
Dmitry Teytelman
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

On Wed, 21 Mar 2007, Duane Clark wrote:

> [email protected] wrote:
>> ...
>> So here is the problem: the data is written correctly into the SRAM, but
>> not into the block RAM. It is a timing problem - errors go away if I lower
>> the clock frequency.
>> ...

>
> Show the actual clock constraints you are using.


The data acquisition logic and BRAMs are clocked by a DCM driving a BUFG.
The DCM divides the input clock by 2 (using CLKDV output). Here is the
timing constraint on the DCM input clock:

NET "clkp" TNM_NET = FFS(*) "clkp";
TIMESPEC "TS_clkp" = PERIOD "clkp" 3.8 ns HIGH 50 %;

--
Dmitry Teytelman
Reply With Quote
  #7 (permalink)  
Old 03-21-2007, 02:38 PM
John_H
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Dmitry Teytelman wrote:
> On Wed, 21 Mar 2007, Duane Clark wrote:
>
>> [email protected] wrote:
>>> ...
>>> So here is the problem: the data is written correctly into the SRAM,
>>> but not into the block RAM. It is a timing problem - errors go away
>>> if I lower
>>> the clock frequency.
>>> ...

>>
>> Show the actual clock constraints you are using.

>
> The data acquisition logic and BRAMs are clocked by a DCM driving a
> BUFG. The DCM divides the input clock by 2 (using CLKDV output). Here is
> the timing constraint on the DCM input clock:
>
> NET "clkp" TNM_NET = FFS(*) "clkp";
> TIMESPEC "TS_clkp" = PERIOD "clkp" 3.8 ns HIGH 50 %;


Get rid of the FFS(*) and see what happens.

The BlockRAM specifically needs RAMS(*) if you're trying to keep other
elements such as multipliers and latches off the timing specification.
I usually end up with something like

NET sysClk TNM_NET = sysClk;

to specify my main clock. In this syntax it applies to all sequential
elements.

- John_H
Reply With Quote
  #8 (permalink)  
Old 03-22-2007, 04:35 AM
Dmitry Teytelman
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

On Wed, 21 Mar 2007, John_H wrote:

>> NET "clkp" TNM_NET = FFS(*) "clkp";
>> TIMESPEC "TS_clkp" = PERIOD "clkp" 3.8 ns HIGH 50 %;

>
> Get rid of the FFS(*) and see what happens.
>
> The BlockRAM specifically needs RAMS(*) if you're trying to keep other
> elements such as multipliers and latches off the timing specification. I
> usually end up with something like
>
> NET sysClk TNM_NET = sysClk;
>
> to specify my main clock. In this syntax it applies to all sequential
> elements.


John,

Thanks a million! Of course that was it, now everything runs fine up to
325 MHz. And my earlier (working) version did not have FFS(*). That is
what happens when you try to get rid of unimportant warnings

--
Dmitry Teytelman
Reply With Quote
  #9 (permalink)  
Old 03-22-2007, 05:36 AM
Daniel S.
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Dmitry Teytelman wrote:
> On Wed, 21 Mar 2007, John_H wrote:
>
>>> NET "clkp" TNM_NET = FFS(*) "clkp";
>>> TIMESPEC "TS_clkp" = PERIOD "clkp" 3.8 ns HIGH 50 %;

>>
>> Get rid of the FFS(*) and see what happens.
>>
>> The BlockRAM specifically needs RAMS(*) if you're trying to keep other
>> elements such as multipliers and latches off the timing specification.
>> I usually end up with something like
>>
>> NET sysClk TNM_NET = sysClk;
>>
>> to specify my main clock. In this syntax it applies to all sequential
>> elements.

>
> John,
>
> Thanks a million! Of course that was it, now everything runs fine up to
> 325 MHz. And my earlier (working) version did not have FFS(*). That is
> what happens when you try to get rid of unimportant warnings


Would the unimportant warnings in question happen to be the one about
PAR/MAP getting confused between PAD and IOB FFs timing constraints? I am
glad I saw this thread because I was about to make the very same mistake to
get rid of those warnings too!
Reply With Quote
  #10 (permalink)  
Old 03-22-2007, 06:03 AM
Dmitry Teytelman
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Hello Daniel,

On Thu, 22 Mar 2007, Daniel S. wrote:

> Would the unimportant warnings in question happen to be the one about PAR/MAP
> getting confused between PAD and IOB FFs timing constraints? I am glad I saw
> this thread because I was about to make the very same mistake to get rid of
> those warnings too!


I added FFS(*) to the constraint to get rid of the following warning in
the translation report:

WARNING:XdmHelpers:662 - Period specification "TS_clk_ctrl_aclk4_dcm"
references the TNM group "clk_ctrl_aclk4_dcm", which contains both pads
and synchronous elements. The timing analyzer will ignore the pads for
this specification. You might want to use a qualifier (e.g. "FFS") on the
TNM property to remove the pads from this group.

--
Dmitry Teytelman
Reply With Quote
  #11 (permalink)  
Old 03-22-2007, 12:49 PM
Martin Thompson
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Dmitry Teytelman <[email protected]> writes:

> Hello Daniel,
>
> On Thu, 22 Mar 2007, Daniel S. wrote:
>
>> Would the unimportant warnings in question happen to be the one
>> about PAR/MAP getting confused between PAD and IOB FFs timing
>> constraints? I am glad I saw this thread because I was about to make
>> the very same mistake to get rid of those warnings too!

>
> I added FFS(*) to the constraint to get rid of the following warning
> in the translation report:
>
> WARNING:XdmHelpers:662 - Period specification "TS_clk_ctrl_aclk4_dcm"
> references the TNM group "clk_ctrl_aclk4_dcm", which contains both
> pads and synchronous elements. The timing analyzer will ignore the
> pads for this specification. You might want to use a qualifier
> (e.g. "FFS") on the TNM property to remove the pads from this group.
>


I reckon that warning ought to end with
"... but you probably don't as this might break all sorts of other
things unless you really understand what this does"

:-)

Xilinx: How about
a) A better warning
b) the ability to say "NOPADS" on the TNM property (or NOFFS, NORAMS
etc...)

Cheers,
Martin

--
[email protected]
TRW Conekt - Consultancy in Engineering, Knowledge and Technology
http://www.conekt.net/electronics.html
Reply With Quote
  #12 (permalink)  
Old 03-22-2007, 02:54 PM
John_H
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Martin Thompson wrote:
> Dmitry Teytelman <[email protected]> writes:
>
>> Hello Daniel,
>>
>> On Thu, 22 Mar 2007, Daniel S. wrote:
>>
>>> Would the unimportant warnings in question happen to be the one
>>> about PAR/MAP getting confused between PAD and IOB FFs timing
>>> constraints? I am glad I saw this thread because I was about to make
>>> the very same mistake to get rid of those warnings too!

>> I added FFS(*) to the constraint to get rid of the following warning
>> in the translation report:
>>
>> WARNING:XdmHelpers:662 - Period specification "TS_clk_ctrl_aclk4_dcm"
>> references the TNM group "clk_ctrl_aclk4_dcm", which contains both
>> pads and synchronous elements. The timing analyzer will ignore the
>> pads for this specification. You might want to use a qualifier
>> (e.g. "FFS") on the TNM property to remove the pads from this group.
>>

>
> I reckon that warning ought to end with
> "... but you probably don't as this might break all sorts of other
> things unless you really understand what this does"
>
> :-)
>
> Xilinx: How about
> a) A better warning
> b) the ability to say "NOPADS" on the TNM property (or NOFFS, NORAMS
> etc...)
>
> Cheers,
> Martin


I know the EXCEPT can be used to define timing groups but I'm not sure
about the TNM_NET. I'd be interested to see if

NET myclk TNM_NET = EXCEPT PADS(*) myclk;
or
NET myclk TNM_NET = ALL EXCEPT PADS(*) myclk;

works. I think ALL may be a proper keyword but I forget the details.

It might be a matter of 1) defining a catchall TNM_NET, then 2)
redefining that timing group with the EXCEPT keyword.

I'd love to see someone spend a few minutes with the constraints guide
and see if they can get that error to go away in one or two statements
to fully define the group. Personally, I don't push my clocks directly
out onto pads (I'll run them though an ODDR2 primitive first) so I don't
end up seeing the warnings.

The syntax

NET myclk TNM_NET = FFS(*) LATCHES(*) MULTS(*) RAMS(*) myclk;

(and any othere synchronous elements I've forgotten) is another approach
that should work.
Reply With Quote
  #13 (permalink)  
Old 03-23-2007, 12:10 AM
Dmitry Teytelman
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

On Thu, 22 Mar 2007, John_H wrote:

> I know the EXCEPT can be used to define timing groups but I'm not sure about
> the TNM_NET. I'd be interested to see if
>
> NET myclk TNM_NET = EXCEPT PADS(*) myclk;
> or
> NET myclk TNM_NET = ALL EXCEPT PADS(*) myclk;
>
> works. I think ALL may be a proper keyword but I forget the details.


Tried both of the above - no go:

ERROR:NgdBuild:765 - "foo.ucf" Line 40: A parsing error has occurred while
reading the constraint file. The value 'EXCEPT' at column 22 is
invalid.

> Personally, I don't push my clocks directly out onto pads (I'll run them
> though an ODDR2 primitive first) so I don't end up seeing the warnings.


My constraint is on an input clock. The differential inputs go directly
into a clock control module which instantiates IBUFDS and feeds it to some
DCMs. So at the top level the input pad signal is the only one which will
propagate the constraint through the DCMs.

> The syntax
>
> NET myclk TNM_NET = FFS(*) LATCHES(*) MULTS(*) RAMS(*) myclk;
>
> (and any othere synchronous elements I've forgotten) is another approach
> that should work.


No such luck:

ERROR:NgdBuild:765 - "foo.ucf" Line 40: A parsing error has occurred while
reading the constraint file. The value 'LATCHES' at column 30 is invalid.

So it seems that the only way to get rid of the warnings would be to pull
the IBUFDS to the top level and place the TNM_NET on its output.

--
Dmitry Teytelman
Reply With Quote
  #14 (permalink)  
Old 03-23-2007, 01:12 AM
Daniel S.
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Dmitry Teytelman wrote:
> Hello Daniel,
>
> On Thu, 22 Mar 2007, Daniel S. wrote:
>
>> Would the unimportant warnings in question happen to be the one about
>> PAR/MAP getting confused between PAD and IOB FFs timing constraints? I
>> am glad I saw this thread because I was about to make the very same
>> mistake to get rid of those warnings too!

>
> I added FFS(*) to the constraint to get rid of the following warning in
> the translation report:
>
> WARNING:XdmHelpers:662 - Period specification "TS_clk_ctrl_aclk4_dcm"
> references the TNM group "clk_ctrl_aclk4_dcm", which contains both pads
> and synchronous elements. The timing analyzer will ignore the pads for
> this specification. You might want to use a qualifier (e.g. "FFS") on
> the TNM property to remove the pads from this group.


Yup, that is the annoying warning I was thinking about. I wish there was a
simple timespec for everything except pads... everything synchronous. From
other replies in this thread, it seems like the only way is exhaustive
enumeration.
Reply With Quote
  #15 (permalink)  
Old 03-23-2007, 02:15 PM
Martin Thompson
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

John_H <[email protected]> writes:

> Martin Thompson wrote:
>> Dmitry Teytelman <[email protected]> writes:
>>
>>> Hello Daniel,
>>>
>>> On Thu, 22 Mar 2007, Daniel S. wrote:
>>>
>>>> Would the unimportant warnings in question happen to be the one
>>>> about PAR/MAP getting confused between PAD and IOB FFs timing
>>>> constraints? I am glad I saw this thread because I was about to make
>>>> the very same mistake to get rid of those warnings too!
>>> I added FFS(*) to the constraint to get rid of the following warning
>>> in the translation report:
>>>
>>> WARNING:XdmHelpers:662 - Period specification "TS_clk_ctrl_aclk4_dcm"
>>> references the TNM group "clk_ctrl_aclk4_dcm", which contains both
>>> pads and synchronous elements. The timing analyzer will ignore the
>>> pads for this specification. You might want to use a qualifier
>>> (e.g. "FFS") on the TNM property to remove the pads from this group.
>>>

>>
>> I reckon that warning ought to end with "... but you probably don't
>> as this might break all sorts of other
>> things unless you really understand what this does"
>>
>> :-)
>>
>> Xilinx: How about
>> a) A better warning
>> b) the ability to say "NOPADS" on the TNM property (or NOFFS, NORAMS
>> etc...)
>>
>> Cheers,
>> Martin

>
> I know the EXCEPT can be used to define timing groups but I'm not sure
> about the TNM_NET. I'd be interested to see if
>


I knew there was something like that, but my quick reread of the
constraints guide didn't point me at it - thanks!

<snip>
> The syntax
>
> NET myclk TNM_NET = FFS(*) LATCHES(*) MULTS(*) RAMS(*) myclk;
>
> (and any othere synchronous elements I've forgotten)


and therein lies the problem :-) Using EXCEPT would allow the tools
to remember for you (we hope..)

Cheers,
Martin

--
[email protected]
TRW Conekt - Consultancy in Engineering, Knowledge and Technology
http://www.conekt.net/electronics.html
Reply With Quote
  #16 (permalink)  
Old 03-23-2007, 04:39 PM
Duane Clark
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Dmitry Teytelman wrote:
> On Wed, 21 Mar 2007, Duane Clark wrote:
>
>> [email protected] wrote:
>>> ... So here is the problem: the data is written correctly into
>>> the SRAM, but not into the block RAM. It is a timing problem -
>>> errors go away if I lower the clock frequency. ...

>> Show the actual clock constraints you are using.

>
> The data acquisition logic and BRAMs are clocked by a DCM driving a
> BUFG. The DCM divides the input clock by 2 (using CLKDV output). Here
> is the timing constraint on the DCM input clock:
>
> NET "clkp" TNM_NET = FFS(*) "clkp"; TIMESPEC "TS_clkp" = PERIOD
> "clkp" 3.8 ns HIGH 50 %;
>


Let me step back from the long thread a moment. According to the
constraints guide:

Assigning Definitions for DLL/DCM Clocks

TRANSLATION (NGDBuild) propagates TNM_NET tags through DLLs and DCMs.
NGDBuild creates new TNM_NETs for each of the DLL and DCM output taps
and associated PERIOD statements. The code takes into account the phase
relationship factor of the outputs for the DLL, and also performs the
appropriate multiplication or division of the PERIOD value.

The code also takes into account any of the PHASE taps adjustments. This
means that for OFFSETs and cross-clock domain paths, the timing tools
now know the relationship for PHASE shifts also.

So have you tried just a period constraint on the input clock and
removed all other constraints?
Reply With Quote
  #17 (permalink)  
Old 03-23-2007, 04:41 PM
Duane Clark
Guest
 
Posts: n/a
Default Re: Virtex-II block RAM problem

Duane Clark wrote:
> Dmitry Teytelman wrote:
>> On Wed, 21 Mar 2007, Duane Clark wrote:
>>
>>> [email protected] wrote:
>>>> ... So here is the problem: the data is written correctly into
>>>> the SRAM, but not into the block RAM. It is a timing problem -
>>>> errors go away if I lower the clock frequency. ...
>>> Show the actual clock constraints you are using.

>> The data acquisition logic and BRAMs are clocked by a DCM driving a
>> BUFG. The DCM divides the input clock by 2 (using CLKDV output). Here
>> is the timing constraint on the DCM input clock:
>>
>> NET "clkp" TNM_NET = FFS(*) "clkp"; TIMESPEC "TS_clkp" = PERIOD
>> "clkp" 3.8 ns HIGH 50 %;
>>

>
> Let me step back from the long thread a moment. According to the
> constraints guide:


Ugg... never mind. I see you did say this was the input clock.
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
problem block ram modelsim devre FPGA 0 03-30-2006 01:02 PM
Block RAM problem (spartan 3) [email protected] FPGA 0 09-11-2005 09:41 PM
Using the Virtex Block Select RAM+ Features Vazquez FPGA 1 11-03-2003 06:41 PM


All times are GMT +1. The time now is 12:02 PM.


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