I am trying to talk to a high-speed serial FRAM part (an FM25V20, lovely device) from a MSP430G2553. Data transfer to and from the RAM device is the major bottleneck on my application, so I want it to be as fast as possible. Unfortunately I'm finding that if I run the MSP430 at above 8MHz I appear to get corrupt transfers. Also some rather weird behaviour that doesn't mean much to me (see below).
I'm doing SPI in hardware in what I think is three-wire mode --- SCLK/MISO/MOSI/CS. The FRAM part says it runs up to 40MHz, so I wouldn't have thought there'd be any problem running it at the maximum speed of the MSP430. The two parts are on breadboard, with the wires connecting them about 30-40mm. Is this fast enough for that to be a problem?
I've spent a lot of time staring at my code and scratching my head; any chance someone could have a glance over and tell me if there's anything obviously wrong?
To set up the MSP430 clocks:
; set MCLK to precalibrated 16MHz from the DCO
clr.b &DCOCTL
mov.b &CALBC1_16MHZ, &BCSCTL1
mov.b &CALDCO_16MHZ, &DCOCTL
; SMCLK (used for SPI) is MCLK/1.
mov.b #DIVS_0|DIVM_0, &BCSCTL2
To initialise the USCI module:
SPI_MODE_3 = UCCKPL
bis.b #RAM_CS, &P1DIR ; RAM chip select
bis.b #RAM_CS, &P1OUT ; set chip select high
bis.b #RAM_SOMI|RAM_SIMO|RAM_SCLK, &P1SEL ; assign pins to peripheral
bis.b #RAM_SOMI|RAM_SIMO|RAM_SCLK, &P1SEL2
bis.b #UCSWRST, &UCB0CTL1 ; Reset USCI
bis.b #SPI_MODE_3|UCMSB|UCMST|UCMODE0|UCSYNC, &UCB0CTL0 ; USCI to SPI mode
bis.b #UCSSEL_2, &UCB0CTL1 ; SMCLK
mov.b #0, &UCB0BR0 ; divider, low byte
mov.b #0, &UCB0BR1 ; divider, high byte
bic.b #UCSWRST, &UCB0CTL1 ; Reenable USCI
To actually do a transfer:
.macro FRAM_W XMIT
mov.b \XMIT, &UCB0TXBUF ; write outgoing
1:
bit.b #UCB0TXIFG, &IFG2
jnc 1b ; wait for transmission
.endm
.macro FRAM_RW XMIT, RECV
mov.b \XMIT, &UCB0TXBUF ; write outgoing
1:
bit.b #UCB0TXIFG, &IFG2
jnc 1b ; wait for transmission
1:
bit.b #UCB0RXIFG, &IFG2
jnc 1b ; wait for reception
mov.b &UCB0RXBUF, \RECV
.endm
readword:
bic.b #RAM_CS, &P1OUT ; enable FRAM
FRAM_W #READ ; opcode
FRAM_W #0x00 ; top byte of address
swpb rSRC
FRAM_W rSRC ; middle byte of address
swpb rSRC
FRAM_W rSRC ; low byte of address
FRAM_RW #0, rMT ; read low byte
FRAM_RW #0, rMEM ; read high byte
swpb rMEM
bis rMT, rMEM
bis.b #RAM_CS, &P1OUT ; disable FRAM
ret
I'm very confused about they way the clocks are behaving. If I set MCLK to 16MHz, then use DIVS_1 in BCSCTL2 to try and make SMCLK 8MHz, nothing works. (Instant corrupt transfer.) Likewise, if I use DIVS_0 in BCSCTL2 and then set the divider in the USCI unit itself --- UCB0BR0 and UCB0BR1 --- then nothing works. If I set MCLK to 8MHz and leave the dividers at 1, it works fine (if slowly). (I don't actually understand UCB0BRx; the manual says that the SPI clock speed is BRCLK/UCB0BRx, except the value which seems to work most often is the default, which is zero...)
If I set MCLK to 12MHz, and the dividers both to 1, then it mostly works --- my code partially runs before it gets a corrupt transfer and dies. But it will frequently die at precisely the same place several times in a row! Which is really weird.
Also, if anyone has any advice on how to speed up the readword routine above, I'd be very grateful; I'm doing a zillion very rapid, very small random access transfers and it needs to be as fast as humanly possible. I'm sure it must be possible to pipeline the xmit/recv code more...