ARM Assembly help (initializing PLL problem; lpc2388)

Hey everybody! So I’ve been mucking through and learning as much as I can about my little ARM7tdmi and I’ve run into a bit of a problem. I wrote some code to boot up my PLL to divide down by 8, multiply up by 160, and then the CPU clock divider divides down by 4 (the main oscillator is 14.318180 MHz). I wrote the following code to boot up my PLL:

/* crt0.S -- The capital S is important! */

/* Take advantage of the fact that we're using the C preprocessor */
#define OSCEN (0x01 << 5)
#define OSCSTAT (0x01 << 6)
#define PLLSTAT (0x01 << 26)
#define SCSSAFE (0x000000FB)

.text
.arm
.extern main
.global _start
_start:
/* Vectors (8 total) */
    b reset /* reset */
    b loop  /* undefined instruction */
    b loop  /* software interrupt */
    b loop  /* prefetch abort */
    b loop  /* data abort */
    .word 0x99fffe4f /* hand-calculated bootloader checksum */
    b loop  /* IRQ */
    b loop  /* FIQ */

/* Setup the C environment: 
  * Setup the PLL
  * Copy .data into SRAM
  * Zero out .bss
  * setup stack pointer
  * Jump to main */
reset:
    /* Setup PLL */
    /* Start by making sure it's disabled! */
    ldr r0, =0
    ldr r1, pllcon_addr
    str r0, [r1]
    /* You'll notice this code a number of times
       It's the "feed sequence" which copies the
       data from the edited registers into the real
       registers somewhere. */
    ldr r0, =0xAA
    ldr r1, =0x55
    ldr r2, pllfeed_addr
    str r0, [r2]
    str r1, [r2]

    /* Activate the main oscillator */
    ldr r0, scs_addr
    ldr r1, [r0]
    orr r1, r1, #OSCEN
    and r1, r1, #SCSSAFE /* Mask out all reserved bits */
    str r1, [r0]

    /* Wait for main oscillator to settle */
    ldr r1, =OSCSTAT
osc_loop:
    ldr r2, [r0]
    and r2, r2, r1
    cmp r2, #0
    beq osc_loop

    /* Switch from the internal clock to the main oscillator */
    ldr r0, =1
    ldr r1, clksrcsel_addr
    str r0, [r1]

    /* Now set up the PLLCFG register */
    ldr r0, =((0x08 << 16) | 160)
    ldr r1, pllcfg_addr
    str r0, [r1]

   /* Now that the PLLCFG register will multiply up by
    160 and divide down by 8, FEEED MEEEEE */
    ldr r0, =0xAA
    ldr r1, =0x55
    ldr r2, pllfeed_addr
    str r0, [r2]
    str r1, [r2]

    /* Now we enable the PLL */
    ldr r0, =1
    ldr r1, pllcon_addr
    str r0, [r1]
    ldr r0, =0xAA
    ldr r1, =0x55
    ldr r2, pllfeed_addr
    str r0, [r2]
    str r1, [r2]

    /* Now we set the divider to match the value it will need after the PLL */
    ldr r0, =3
    ldr r1, cclkcfg_addr
    str r0, [r1]

    /* Now we loop while we wait fot the PLL output to stabalize */
    ldr r0, =PLLSTAT
    ldr r1, pllstat_addr
pll_loop:
    ldr r2, [r1]
    and r2, r0
    cmp r2, #0
    beq pll_loop

    /* Now that the PLL has locked we simply connect it up */
    ldr r0, =3
    ldr r1, pllcon_addr
    ldr r0, [r1]
    ldr r0, =0xAA
    ldr r1, =0x55
    ldr r2, pllfeed_addr
    str r0, [r2]
    str r1, [r2]

    /* PLL setup complete! Hope you don't see any magic smoke! */

    /***************************************************/
    /* I removed all the stuff which copies everything into RAM */
    /***************************************************/

/* Constants:*/
/* System Controls and Status register */
scs_addr:     .word 0xe01fc1a0

/* PLL constants */
clksrcsel_addr: .word 0xe01fc10c
pllcon_addr:    .word 0xe01fc080
pllcfg_addr:    .word 0xe01fc084
pllstat_addr:   .word 0xe01fc088
pllfeed_addr:   .word 0xe01fc08c

/* Clock divider constants */
cclkcfg_addr: .word 0xe01fc104

Can anyone spot what I’m missing? Currently the processor seems like it boots up fine, and is running slightly slower than it would had I not run the PLL at all, which would make sense if it was running directly from the 14.31818MHz oscillator then dividing down by 4 (giving ~3.57MHz. A smidge under 4MHz).

One thing which is confusing me is that when I step through “ldr r1, =OSCSTAT” I get the message (in openOCD):

"address + size wrapped(0xfffffffe, 0x00000004)

address + size wrapped(0xffffffff, 0x00000004)"

(Or maybe with the pipeline it could be “and r1, r1, #SCSSAFE” or “str r1, [r0]”), which almost sounds like it’s playing with the uninitialized stack?

Something weird is going on! I don’t know what it is! Any help/suggestions?

Thanks!

Any particular reason for using assembly rather than C?

Well this is in my crt0.S file. I’m not using a pre-made board so I have to write my own initialization scripts (which sucks, no one seems to document how to do this part!). The whole point of this is to hand off a ready-to-go microcontroller to the main routine written in C. It does that part quite well, save for the PLL initialization.

I guess I could write this chunk as the first part of the main routine, but I’d like to not have to muck around with the oscillator once the C code is in control. It’s also just because I’m paranoid that I’ll screw up the PLL feed sequence once I’m not doing assembly and the processor is allowed to insert instructions which make it take more than 2 instructions (as per the datasheet, the feed sequence must take exactly two instructions).

I’ve started combing through the LPC23xx.s file which comes with the example USB and ethernet bootloader but I’m not spotting my mistake… any suggestions? Can anyone explain why I’m getting that “address + size wrapped(0xfffffffe, 0x00000004)” error?

By the way, stevech: Thanks for responding :smiley: ! You’re the first person to say anything in response to a thread I’ve started in this forum (you can see I have one which was essentially me talking to myself a few months ago)!

I would still suggest doing the initialization down in C-land. You can always inline some assembler for the critical two-instruction part. Debugging tools are unlikely to be happy with debugging crt0 because it hasn’t done all those crt0 things you can see in the comments, like set up the stack. If running crt0 with the PLL disabled is a big performance hit for you, you can always experiment with pushing the code up into crt0 after you get it running in C.

The comments are things I did removed because they weren’t important to someone being able to help with this issue I’m having. I wrote those parts already, and they work just fine. OpenOCD (and by extension the arm-usb-ocd) doesn’t care if stuff like the stack being set up because the thing is a hardware debugger, it’s pretty happy to debug unless I actually break the device. I can already single-step through that code and watch as each instruction goes by, and compare the instructions as they are written into memory to the instructions in crt0.S inside arm-elf-gdb. I don’t really know what advantage, aside from perhaps slightly clearer code, I would get from initializing the PLL and main oscillator in C… In fact, an upside to writing it in assembly is I can see whether or not the assembler translated any of the “ldr r0, #0x123456” statements weirdly, or if I’m failing to program the chip properly!

Could this be part of the problem: [lpc2388 errata? If you have a 14MHz clock input and you multiply it up by 40 (i.e., 2*160/8) you get a CCO frequency greater than the pre-erratum max, in fact.

The feed sequence does not have to be two instructions long. It just has to happen without other accesses to that address space between the two writes, so it’s quite feasible to do the whole thing in C, if you find C easier to write and debug.

So what’s in PLL0STAT after you do all this? That might tell you something (e.g., whether the PLL is enabled, whether the feed sequence worked, and such).](http://ics.nxp.com/support/documents/microcontrollers/pdf/errata.lpc2388.pdf)

Here’s an idea: Download the IAR product for ARM. It’s free for 32KB and smaller programs.

kitscuzz:
I wrote some code to boot up my PLL to divide down by 8, multiply up by 160, and then the CPU clock divider divides down by 4 (the main oscillator is 14.318180 MHz).

I didn’t notice this earlier, sorry. You actually multiplied by 322. You should look closely at the diagram of the PLL; there’s a Secret Divide By Two in there. Moreover, if you read the description of the PLL0CONF register, you store M-1 and N-1 into that register. Hope this helps.

Wow! Lots of helpful posts. Alright, so I started by realizing that I had butchered the calculations for the PLL.

So I was going for about as close to the 72MHz as I could get. I have a 14.318180MHz oscillator. So the new calculation is that I want an FCCO of 286.3636MHz, using the formula for M=(FCCON)/(2F_IN) with an N of 1 I got M=10. With a CPU divide of 4 this gives 71.5909MHz.

tecoist:
Could this be part of the problem: lpc2388 errata? If you have a 14MHz clock input and you multiply it up by 40 (i.e., 2*160/8) you get a CCO frequency greater than the pre-erratum max, in fact.

This is actually Rev B of the silicon, so it should be fine.

stevech:
Here’s an idea: Download the IAR product for ARM. It’s free for 32KB and smaller programs.

I already have a working compiler and debugger which have no restrictions! I compiled the GNU toolchain and newlib myself, they work just fine. Also, does the IAR toolchain even run on linux? I couldn’t find the requirements anywhere.

stevech:
Maybe this example code for a certain family of NXP ARM7 will help?

I’m looking through this now to see if there’s anything I’m missing.

tecoist:
I didn’t notice this earlier, sorry. You actually multiplied by 322.

Indeed! As previously mentioned I messed up the calculations, your post made me re-look at all the material and make that work.

Alright, so now I seem to have the correct value for M (I load in 9 to PLLCFG). As so many recommended, I looked at the PLLSTAT configuration, and it appears to be correct, save for one thing: the PLLC bit is cleared! I have 0x05000009 in PLLSTAT. I’ll keep mucking around with it.

I attached a slightly updated version of my new crt0.S

HAAAAAAAAAAAAAAAAAAAA! I’m an idiot! WOOOOOOOOOO!

    /* Now that the PLL has locked we simply connect it up */
    ldr r0, =3
    ldr r1, pllcon_addr 
    ldr r0, [r1] /* Anyone notice the what's wrong with this step???? */
    ldr r0, =0xAA
    ldr r1, =0x55
    ldr r2, pllfeed_addr
    str r0, [r2]
    str r1, [r2]

:oops: :oops: :oops: :oops:

Yes, I put an ldr when I meant to put an str there.

Anyway, it booted up and ran fine! Thanks everybody for all the help getting all this running!

So as a side question, does anyone know why loading the code would sometimes fail? I’m having a weird problem where now that I’ve set up the PLL I seem to be able to write the code sometimes, but if I write it while it’s running at ~72MHz it will write gibberish to the chip. I have to kill it, and while it’s running at 4MHz re-write the code.

By “write the code” do you mean program the flash?

The things I’ve run into (some with other ARMs):

  • forget to reconfigure the flash read interface for the new clock speed.

  • try to read flash during programming, causing watchdog crash-and-burn,

  • tell the flash programming code terrible untruths about the clock speed.

Just what I’ve seen and in some cases done.

tecoist:
By “write the code” do you mean program the flash?

Yes.

tecoist:

  • forget to reconfigure the flash read interface for the new clock speed.

It seems to run fine at the new clock speed, but I can’t program it at the new clock speed.

tecoist:

  • try to read flash during programming, causing watchdog crash-and-burn

I always do a soft_reset_halt before I program it.

tecoist:

  • tell the flash programming code terrible untruths about the clock speed.

Haaaaaahahahaha :stuck_out_tongue: I like the way that sounds. Anyway, I’m programming using an arm-usb-ocd over JTAG with openocd, so I would have assumed that as long as it was able to talk to the chip (it can still single step through the code), it could write to the flash.

kitscuzz:
I would have assumed that as long as it was able to talk to the chip (it can still single step through the code), it could write to the flash.

Bet it times the write cycle using the happy cpu clock. I don't think you can just scan it in and push the Wahoo Flash Write Button. I don't know for sure, but I did notice that the old script I have for openOCD flash on the 23xx carefully disables the PLL and selects the internal RC oscillator before it tries to do anything to the flash.