The purpose of this group is to foster exchange of information on the Texas Instruments MSP430 family of microcontrollers and related tools. Everyone welcome, all levels of familiarity/expertise.
Erasing INFO Memory Discovery - marlfox0415 - Aug 20 10:45:59 2008
First of all, thank you for the responses. I haven't had a chance to
try your methods yet, but I will give them a shot. I discovered why
my code was breaking and wanted to ask if anyone had any idea why
this was happening.
Apparently, it's not just erasing INFO memory but also my Code
memory, over-writing it with 3FFF. This is nuking my call stack,
which is why my program hangs when it tries to exit the function.
I've made sure that the MERAS bit is not set for FCTL1, so I'm not
sure what else it could be. I'll paste my current code below. Thank
you.
-John
void erase_segment(char *segment_address) {
FCTL2 = FWKEY + FSSEL1 + FN0;
FCTL3 = FWKEY;
FCTL1 = FWKEY + ERASE;
*(segment_address) = 0;
FCTL1 = FWKEY;
FCTL3 = FWKEY + LOCK;
return;
}
------------------------------------

(You need to be a member of msp430 -- send a blank email to msp430-subscribe@yahoogroups.com )
Re: Erasing INFO Memory Discovery - Jerry Yu - Aug 21 0:35:45 2008
Dear ,
you need add a sentence: while(FCTL3&0x0001); behind dummy write:
*(segment_address) = 0;.
So, the code is,
void erase_segment( char *segment_address) {
FCTL2 = FWKEY + FSSEL1 + FN0;
FCTL3 = FWKEY;
FCTL1 = FWKEY + ERASE;
*(segment_address) = 0;
while(FCTL3&0x0001); // busy?
FCTL1 = FWKEY;
FCTL3 = FWKEY + LOCK;
return;
}
thanks!
----- Original Message ----
From: marlfox0415
To: m...@yahoogroups.com
Sent: Wednesday, August 20, 2008 10:45:50 PM
Subject: [msp430] Erasing INFO Memory Discovery
First of all, thank you for the responses. I haven't had a chance to
try your methods yet, but I will give them a shot. I discovered why
my code was breaking and wanted to ask if anyone had any idea why
this was happening.
Apparently, it's not just erasing INFO memory but also my Code
memory, over-writing it with 3FFF. This is nuking my call stack,
which is why my program hangs when it tries to exit the function.
I've made sure that the MERAS bit is not set for FCTL1, so I'm not
sure what else it could be. I'll paste my current code below. Thank
you.
-John
void erase_segment( char *segment_address) {
FCTL2 = FWKEY + FSSEL1 + FN0;
FCTL3 = FWKEY;
FCTL1 = FWKEY + ERASE;
*(segment_address) = 0;
FCTL1 = FWKEY;
FCTL3 = FWKEY + LOCK;
return;
}
[Non-text portions of this message have been removed]
------------------------------------

(You need to be a member of msp430 -- send a blank email to msp430-subscribe@yahoogroups.com )Re: Erasing INFO Memory Discovery - Hugh Molesworth - Aug 21 11:33:59 2008
Your observation about the busy wait only applies to flash
write/erase code running from within RAM, not to code running within
flash since in the latter case the MSP cpu automatically executes NOP
instructions until the flash write completes. In this case I would
hazard a guess that if indeed code flash is being erased as well as
info flash that the flash is being thrashed to death by a completely
incorrect clock setting. So, how does the clock setting work?
"Write and erase operations are controlled by the flash timing
generator shown in Figure 5-3. The flash timing generator operating
frequency, f(FTG), must be in the range from ~ 257 kHz to ~ 476 kHz
(see device-specific datasheet)."
"FNx Bits 5-0 Flash controller clock divider. These six bits select
the divider for the flash controller clock. The divisor value is FNx
+ 1. For example, when FNx=00h, the divisor is 1. When FNx=03Fh the
divisor is 64."
These are the typical definitions of FN0 etc:
#define FN0 (0x0001) /* Divide Flash clock by 1 to 64 using FN0 to
FN5 according to: */
#define FN1 (0x0002) /* 32*FN5 + 16*FN4 + 8*FN3 + 4*FN2 + 2*FN1 + FN0 + 1 */
Your code is using the following:
FCTL2 = FWKEY + FSSEL1 + FN0;
This implies MCLK/2 as the clock source (since FSSEL1=1, MCLK), so
for this write/erase to be working within specification your MCLK
must be in the range 257kHz*2 to 476kHz*2, or 514kHz to 952kHz. I bet
you a beer that your MCLK is nowhere near the range 514kHz to 952kHz.
Even using the default value of the DCO won't put you there.
This is a more typical setting for an MCLK of 8MHz, as an example:
// Flash clock MUST be in range 257kHz - 476kHz
#define FLASH_WRITE_TIME (26-1) // 8MHz MCLK/n for 307kHz
FCTL2 = FWKEY | FSSEL0 | FLASH_WRITE_TIME; // MCLK/n for Flash
Timing Generator
This is a more typical setting for an MCLK of the default DCO value
of 1MHz, center range, as another example. Since the DCO is very
unstable, better to choose as close to the center of the allowed
range as you can get.
// Flash clock MUST be in range 257kHz - 476kHz
#define FLASH_WRITE_TIME (3-1) // 1MHz MCLK/n using DCO, set to
center 333kHz
FCTL2 = FWKEY | FSSEL0 | FLASH_WRITE_TIME; // MCLK/n for Flash
Timing Generator
Why am I using "|", surely it's just the same as "+"? No, it isn't,
but you will only discover this when using an incorrect definition of
one of the bit fields which then overflows into other bits making
debugging extremely difficult.
Hugh
At 09:35 PM 8/20/2008, you wrote:
Dear ,
you need add a sentence: while(FCTL3&0x0001); behind dummy write:
*(segment_address) = 0;.
So, the code is,
void erase_segment( char *segment_address) {
FCTL2 = FWKEY + FSSEL1 + FN0;
FCTL3 = FWKEY;
FCTL1 = FWKEY + ERASE;
*(segment_address) = 0;
while(FCTL3&0x0001); // busy?
FCTL1 = FWKEY;
FCTL3 = FWKEY + LOCK;
return;
}
thanks!
----- Original Message ----
From: marlfox0415
To: m...@yahoogroups.com
Sent: Wednesday, August 20, 2008 10:45:50 PM
Subject: [msp430] Erasing INFO Memory Discovery
First of all, thank you for the responses. I haven't had a chance to
try your methods yet, but I will give them a shot. I discovered why
my code was breaking and wanted to ask if anyone had any idea why
this was happening.
Apparently, it's not just erasing INFO memory but also my Code
memory, over-writing it with 3FFF. This is nuking my call stack,
which is why my program hangs when it tries to exit the function.
I've made sure that the MERAS bit is not set for FCTL1, so I'm not
sure what else it could be. I'll paste my current code below. Thank
you.
-John
void erase_segment( char *segment_address) {
FCTL2 = FWKEY + FSSEL1 + FN0;
FCTL3 = FWKEY;
FCTL1 = FWKEY + ERASE;
*(segment_address) = 0;
FCTL1 = FWKEY;
FCTL3 = FWKEY + LOCK;
return;
}
------------------------------------

(You need to be a member of msp430 -- send a blank email to msp430-subscribe@yahoogroups.com )Re: Erasing INFO Memory Discovery - old_cow_yellow - Aug 21 18:27:43 2008
As far as I know, FCLK being too slow will shorten the expected life
of the FLASH. FCLK being too fast will result in incomplete (and
unreliable) erase/write. It is unlikely that this will erase/write the
unintended segments of the FLASH.
--- In m...@yahoogroups.com, Hugh Molesworth
wrote:
>
> Your observation about the busy wait only applies to flash
> write/erase code running from within RAM, not to code running within
> flash since in the latter case the MSP cpu automatically executes NOP
> instructions until the flash write completes. In this case I would
> hazard a guess that if indeed code flash is being erased as well as
> info flash that the flash is being thrashed to death by a completely
> incorrect clock setting. So, how does the clock setting work?
>
> "Write and erase operations are controlled by the flash timing
> generator shown in Figure 5-3. The flash timing generator operating
> frequency, f(FTG), must be in the range from ~ 257 kHz to ~ 476 kHz
> (see device-specific datasheet)."
>
> "FNx Bits 5-0 Flash controller clock divider. These six bits select
> the divider for the flash controller clock. The divisor value is FNx
> + 1. For example, when FNx=00h, the divisor is 1. When FNx=03Fh the
> divisor is 64."
>
> These are the typical definitions of FN0 etc:
> #define FN0 (0x0001) /* Divide Flash clock by 1 to 64 using FN0 to
> FN5 according to: */
> #define FN1 (0x0002) /* 32*FN5 + 16*FN4 + 8*FN3 + 4*FN2 + 2*FN1 +
FN0 + 1 */
>
> Your code is using the following:
> FCTL2 = FWKEY + FSSEL1 + FN0;
>
> This implies MCLK/2 as the clock source (since FSSEL1=1, MCLK), so
> for this write/erase to be working within specification your MCLK
> must be in the range 257kHz*2 to 476kHz*2, or 514kHz to 952kHz. I bet
> you a beer that your MCLK is nowhere near the range 514kHz to 952kHz.
> Even using the default value of the DCO won't put you there.
>
> This is a more typical setting for an MCLK of 8MHz, as an example:
> // Flash clock MUST be in range 257kHz - 476kHz
> #define FLASH_WRITE_TIME (26-1) // 8MHz MCLK/n for 307kHz
> FCTL2 = FWKEY | FSSEL0 | FLASH_WRITE_TIME; // MCLK/n for Flash
> Timing Generator
>
> This is a more typical setting for an MCLK of the default DCO value
> of 1MHz, center range, as another example. Since the DCO is very
> unstable, better to choose as close to the center of the allowed
> range as you can get.
> // Flash clock MUST be in range 257kHz - 476kHz
> #define FLASH_WRITE_TIME (3-1) // 1MHz MCLK/n using DCO, set to
> center 333kHz
> FCTL2 = FWKEY | FSSEL0 | FLASH_WRITE_TIME; // MCLK/n for Flash
> Timing Generator
>
> Why am I using "|", surely it's just the same as "+"? No, it isn't,
> but you will only discover this when using an incorrect definition of
> one of the bit fields which then overflows into other bits making
> debugging extremely difficult.
>
> Hugh
>
> At 09:35 PM 8/20/2008, you wrote:
> Dear ,
> you need add a sentence: while(FCTL3&0x0001); behind dummy write:
> *(segment_address) = 0;.
> So, the code is,
>
> void erase_segment( char *segment_address) {
> FCTL2 = FWKEY + FSSEL1 + FN0;
> FCTL3 = FWKEY;
> FCTL1 = FWKEY + ERASE;
>
> *(segment_address) = 0;
> while(FCTL3&0x0001); // busy?
>
> FCTL1 = FWKEY;
> FCTL3 = FWKEY + LOCK;
> return;
> }
>
> thanks!
>
> ----- Original Message ----
> From: marlfox0415
> To: m...@yahoogroups.com
> Sent: Wednesday, August 20, 2008 10:45:50 PM
> Subject: [msp430] Erasing INFO Memory Discovery
> First of all, thank you for the responses. I haven't had a chance to
> try your methods yet, but I will give them a shot. I discovered why
> my code was breaking and wanted to ask if anyone had any idea why
> this was happening.
>
> Apparently, it's not just erasing INFO memory but also my Code
> memory, over-writing it with 3FFF. This is nuking my call stack,
> which is why my program hangs when it tries to exit the function.
> I've made sure that the MERAS bit is not set for FCTL1, so I'm not
> sure what else it could be. I'll paste my current code below. Thank
> you.
>
> -John
>
> void erase_segment( char *segment_address) {
> FCTL2 = FWKEY + FSSEL1 + FN0;
> FCTL3 = FWKEY;
> FCTL1 = FWKEY + ERASE;
>
> *(segment_address) = 0;
>
> FCTL1 = FWKEY;
> FCTL3 = FWKEY + LOCK;
> return;
> }
>
------------------------------------

(You need to be a member of msp430 -- send a blank email to msp430-subscribe@yahoogroups.com )Re: Erasing INFO Memory Discovery - Matthias Weingart - Aug 22 4:40:13 2008
> Apparently, it's not just erasing INFO memory but also my Code
> memory, over-writing it with 3FFF. This is nuking my call stack,
> which is why my program hangs when it tries to exit the function.
> I've made sure that the MERAS bit is not set for FCTL1, so I'm not
> sure what else it could be. I'll paste my current code below. Thank
> you.
My guess is a insufficient power supply.
M.
------------------------------------

(You need to be a member of msp430 -- send a blank email to msp430-subscribe@yahoogroups.com )
Re: Erasing INFO Memory Discovery - Hugh Molesworth - Aug 22 17:15:31 2008
Yes, I agree, though the flash on the MSP was something of a black
art until TI disclosed the full details of operation (in particular
the effect of Tcwt). Anyway (John) I think I have your answer as to
why the main segment is being unexpectedly "erased", and the answer
is that it not, but you are seeing an artifact of your IDE debug. The
key is the "erased" value of 0x3FFF; I knew this was in the user
guide somewhere, and here it is:
"When a byte/word write or any erase operation is initiated from
within flash memory, the flash controller returns op-code 03FFFh to
the CPU at the next instruction fetch. Op-code 03FFFh is the JMP PC
instruction. This causes the CPU to loop until the flash operation is
finished. When the operation is finished and BUSY=0, the flash
controller allows the CPU to fetch the proper op-code and program
execution resumes."
So, my guess is that you are looking at the instruction trace history
of these 0x3FFF NOP instructions, and not the true contents of flash.
Hugh
At 03:27 PM 8/21/2008, you wrote:
As far as I know, FCLK being too slow will shorten the expected life
of the FLASH. FCLK being too fast will result in incomplete (and
unreliable) erase/write. It is unlikely that this will erase/write the
unintended segments of the FLASH.
--- In m...@yahoogroups.com, Hugh Molesworth
wrote:
>
> Your observation about the busy wait only applies to flash
> write/erase code running from within RAM, not to code running within
> flash since in the latter case the MSP cpu automatically executes NOP
> instructions until the flash write completes. In this case I would
> hazard a guess that if indeed code flash is being erased as well as
> info flash that the flash is being thrashed to death by a completely
> incorrect clock setting. So, how does the clock setting work?
>
> "Write and erase operations are controlled by the flash timing
> generator shown in Figure 5-3. The flash timing generator operating
> frequency, f(FTG), must be in the range from ~ 257 kHz to ~ 476 kHz
> (see device-specific datasheet)."
>
> "FNx Bits 5-0 Flash controller clock divider. These six bits select
> the divider for the flash controller clock. The divisor value is FNx
> + 1. For example, when FNx=00h, the divisor is 1. When FNx=03Fh the
> divisor is 64."
>
> These are the typical definitions of FN0 etc:
> #define FN0 (0x0001) /* Divide Flash clock by 1 to 64 using FN0 to
> FN5 according to: */
> #define FN1 (0x0002) /* 32*FN5 + 16*FN4 + 8*FN3 + 4*FN2 + 2*FN1 +
FN0 + 1 */
>
> Your code is using the following:
> FCTL2 = FWKEY + FSSEL1 + FN0;
>
> This implies MCLK/2 as the clock source (since FSSEL1=1, MCLK), so
> for this write/erase to be working within specification your MCLK
> must be in the range 257kHz*2 to 476kHz*2, or 514kHz to 952kHz. I bet
> you a beer that your MCLK is nowhere near the range 514kHz to 952kHz.
> Even using the default value of the DCO won't put you there.
>
> This is a more typical setting for an MCLK of 8MHz, as an example:
> // Flash clock MUST be in range 257kHz - 476kHz
> #define FLASH_WRITE_TIME (26-1) // 8MHz MCLK/n for 307kHz
> FCTL2 = FWKEY | FSSEL0 | FLASH_WRITE_TIME; // MCLK/n for Flash
> Timing Generator
>
> This is a more typical setting for an MCLK of the default DCO value
> of 1MHz, center range, as another example. Since the DCO is very
> unstable, better to choose as close to the center of the allowed
> range as you can get.
> // Flash clock MUST be in range 257kHz - 476kHz
> #define FLASH_WRITE_TIME (3-1) // 1MHz MCLK/n using DCO, set to
> center 333kHz
> FCTL2 = FWKEY | FSSEL0 | FLASH_WRITE_TIME; // MCLK/n for Flash
> Timing Generator
>
> Why am I using "|", surely it's just the same as "+"? No, it isn't,
> but you will only discover this when using an incorrect definition of
> one of the bit fields which then overflows into other bits making
> debugging extremely difficult.
>
> Hugh
>
> At 09:35 PM 8/20/2008, you wrote:
> Dear ,
> you need add a sentence: while(FCTL3&0x0001); behind dummy write:
> *(segment_address) = 0;.
> So, the code is,
>
> void erase_segment( char *segment_address) {
> FCTL2 = FWKEY + FSSEL1 + FN0;
> FCTL3 = FWKEY;
> FCTL1 = FWKEY + ERASE;
>
> *(segment_address) = 0;
> while(FCTL3&0x0001); // busy?
>
> FCTL1 = FWKEY;
> FCTL3 = FWKEY + LOCK;
> return;
> }
>
> thanks!
>
> ----- Original Message ----
> From: marlfox0415
> To: m...@yahoogroups.com
> Sent: Wednesday, August 20, 2008 10:45:50 PM
> Subject: [msp430] Erasing INFO Memory Discovery
>
>
> First of all, thank you for the responses. I haven't had a chance to
> try your methods yet, but I will give them a shot. I discovered why
> my code was breaking and wanted to ask if anyone had any idea why
> this was happening.
>
> Apparently, it's not just erasing INFO memory but also my Code
> memory, over-writing it with 3FFF. This is nuking my call stack,
> which is why my program hangs when it tries to exit the function.
> I've made sure that the MERAS bit is not set for FCTL1, so I'm not
> sure what else it could be. I'll paste my current code below. Thank
> you.
>
> -John
>
> void erase_segment( char *segment_address) {
> FCTL2 = FWKEY + FSSEL1 + FN0;
> FCTL3 = FWKEY;
> FCTL1 = FWKEY + ERASE;
>
> *(segment_address) = 0;
>
> FCTL1 = FWKEY;
> FCTL3 = FWKEY + LOCK;
> return;
> }
>
------------------------------------

(You need to be a member of msp430 -- send a blank email to msp430-subscribe@yahoogroups.com )Re: Erasing INFO Memory Discovery - marlfox0415 - Aug 25 10:48:12 2008
Interesting. Thanks for looking into it, I think you may be correct.
I have since discovered that this problem only occurs when I debug.
If I run the code without break-pointing, it works just fine. So much
for testing code before using it. :)
Thanks again!
-John
--- In m...@yahoogroups.com, Hugh Molesworth
wrote:
>
> Yes, I agree, though the flash on the MSP was something of a black
> art until TI disclosed the full details of operation (in particular
> the effect of Tcwt). Anyway (John) I think I have your answer as to
> why the main segment is being unexpectedly "erased", and the answer
> is that it not, but you are seeing an artifact of your IDE debug.
The
> key is the "erased" value of 0x3FFF; I knew this was in the user
> guide somewhere, and here it is:
>
> "When a byte/word write or any erase operation is initiated from
> within flash memory, the flash controller returns op-code 03FFFh to
> the CPU at the next instruction fetch. Op-code 03FFFh is the JMP PC
> instruction. This causes the CPU to loop until the flash operation
is
> finished. When the operation is finished and BUSY=0, the flash
> controller allows the CPU to fetch the proper op-code and program
> execution resumes."
>
> So, my guess is that you are looking at the instruction trace
history
> of these 0x3FFF NOP instructions, and not the true contents of
flash.
>
> Hugh
>
> At 03:27 PM 8/21/2008, you wrote:
> As far as I know, FCLK being too slow will shorten the expected life
> of the FLASH. FCLK being too fast will result in incomplete (and
> unreliable) erase/write. It is unlikely that this will erase/write
the
> unintended segments of the FLASH.
>
> --- In m...@yahoogroups.com, Hugh Molesworth
wrote:
> >
> > Your observation about the busy wait only applies to flash
> > write/erase code running from within RAM, not to code running
within
> > flash since in the latter case the MSP cpu automatically
executes NOP
> > instructions until the flash write completes. In this case I
would
> > hazard a guess that if indeed code flash is being erased as well
as
> > info flash that the flash is being thrashed to death by a
completely
> > incorrect clock setting. So, how does the clock setting work?
> >
> > "Write and erase operations are controlled by the flash timing
> > generator shown in Figure 5-3. The flash timing generator
operating
> > frequency, f(FTG), must be in the range from ~ 257 kHz to ~ 476
kHz
> > (see device-specific datasheet)."
> >
> > "FNx Bits 5-0 Flash controller clock divider. These six bits
select
> > the divider for the flash controller clock. The divisor value is
FNx
> > + 1. For example, when FNx=00h, the divisor is 1. When FNx=03Fh
the
> > divisor is 64."
> >
> > These are the typical definitions of FN0 etc:
> > #define FN0 (0x0001) /* Divide Flash clock by 1 to 64 using FN0
to
> > FN5 according to: */
> > #define FN1 (0x0002) /* 32*FN5 + 16*FN4 + 8*FN3 + 4*FN2 +
2*FN1 +
> FN0 + 1 */
> >
> > Your code is using the following:
> > FCTL2 = FWKEY + FSSEL1 + FN0;
> >
> > This implies MCLK/2 as the clock source (since FSSEL1=1, MCLK),
so
> > for this write/erase to be working within specification your MCLK
> > must be in the range 257kHz*2 to 476kHz*2, or 514kHz to 952kHz.
I bet
> > you a beer that your MCLK is nowhere near the range 514kHz to
952kHz.
> > Even using the default value of the DCO won't put you there.
> >
> > This is a more typical setting for an MCLK of 8MHz, as an
example:
> > // Flash clock MUST be in range 257kHz - 476kHz
> > #define FLASH_WRITE_TIME (26-1) // 8MHz MCLK/n for 307kHz
> > FCTL2 = FWKEY | FSSEL0 | FLASH_WRITE_TIME; // MCLK/n for Flash
> > Timing Generator
> >
> > This is a more typical setting for an MCLK of the default DCO
value
> > of 1MHz, center range, as another example. Since the DCO is very
> > unstable, better to choose as close to the center of the allowed
> > range as you can get.
> > // Flash clock MUST be in range 257kHz - 476kHz
> > #define FLASH_WRITE_TIME (3-1) // 1MHz MCLK/n using DCO, set
to
> > center 333kHz
> > FCTL2 = FWKEY | FSSEL0 | FLASH_WRITE_TIME; // MCLK/n for Flash
> > Timing Generator
> >
> > Why am I using "|", surely it's just the same as "+"? No, it
isn't,
> > but you will only discover this when using an incorrect
definition of
> > one of the bit fields which then overflows into other bits making
> > debugging extremely difficult.
> >
> > Hugh
> >
> > At 09:35 PM 8/20/2008, you wrote:
> > Dear ,
> > you need add a sentence: while(FCTL3&0x0001); behind dummy write:
> > *(segment_address) = 0;.
> > So, the code is,
> >
> > void erase_segment( char *segment_address) {
> > FCTL2 = FWKEY + FSSEL1 + FN0;
> > FCTL3 = FWKEY;
> > FCTL1 = FWKEY + ERASE;
> >
> > *(segment_address) = 0;
> > while(FCTL3&0x0001); // busy?
> >
> > FCTL1 = FWKEY;
> > FCTL3 = FWKEY + LOCK;
> > return;
> > }
> >
> > thanks!
> >
> > ----- Original Message ----
> > From: marlfox0415
> > To: m...@yahoogroups.com
> > Sent: Wednesday, August 20, 2008 10:45:50 PM
> > Subject: [msp430] Erasing INFO Memory Discovery
> >
> >
> > First of all, thank you for the responses. I haven't had a
chance to
> > try your methods yet, but I will give them a shot. I discovered
why
> > my code was breaking and wanted to ask if anyone had any idea why
> > this was happening.
> >
> > Apparently, it's not just erasing INFO memory but also my Code
> > memory, over-writing it with 3FFF. This is nuking my call stack,
> > which is why my program hangs when it tries to exit the function.
> > I've made sure that the MERAS bit is not set for FCTL1, so I'm
not
> > sure what else it could be. I'll paste my current code below.
Thank
> > you.
> >
> > -John
> >
> > void erase_segment( char *segment_address) {
> > FCTL2 = FWKEY + FSSEL1 + FN0;
> > FCTL3 = FWKEY;
> > FCTL1 = FWKEY + ERASE;
> >
> > *(segment_address) = 0;
> >
> > FCTL1 = FWKEY;
> > FCTL3 = FWKEY + LOCK;
> > return;
> > }
>
------------------------------------

(You need to be a member of msp430 -- send a blank email to msp430-subscribe@yahoogroups.com )