Shop OBEX P1 Docs P2 Docs Learn Events
CLKFREQ returns 4 — Parallax Forums

CLKFREQ returns 4

Daniel NagyDaniel Nagy Posts: 26
edited 2015-03-09 08:43 in Propeller 1
Hello,

In PropGCC I'm implementing a simple millisecond timer that runs in a separate cog.
By using CLKFREQ it does not work. After debugging it turns out CLKFREQ returns 4(?), in a system running at 80MHz.

The main call is like this:
cog_run(&clockCycle, 10); 

The called function looks like this:
// .h
#ifndef CLOCK_H
#define CLOCK_H

#include <propeller.h>

volatile int missionTime;
void clockCycle();

#endif

// .c
#include "clock.h"


void clockCycle(){
    int dT = CLKFREQ / 1000; //doesn't work, because CLKFREQ returns 4. If I supplement it with dT = 80000 then all is fine. 
    int T = CNT;   
    
    while(1){
        T += dT;
        waitcnt(T);
        missionTime++;        
    }
            
}

I have some faded memories about the actual clock freq is somewhere in memory, and CLKFREQ reads that out, but what overwrites it, if it is the case here? Where does 4 come from? :)

Comments

  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-04 11:38
    CLKFREQ comes from the long stored at location 0. If you are getting a value of 4 it sounds like location 0 got overwritten. Maybe you have an uninitialized pointer with a value of 0, and you used it to write something to memory.

    EDIT: Also the second parameter in your call to cog_run is 10. I believe this should be a pointer to a stack.

    EDIT2: The SimpleTools documentation says that the second parameter is the stack size, and it recommends a value of at least 40. As long as you have enough memory I would go with a larger stack size, such as 100 or even 200. A value of 10 is way to small.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-04 11:58
    The source for cog_run is as follows:
    int *cog_run(void (*function)(void *par), int stacksize)
    {
      int *addr;
      addr = malloc(stacksize += 4 + 176 + (stacksize * 4));
      *addr = 1 + cogstart(function, NULL, addr + 4, stacksize - 4);
      if(*addr == 0)
      {
        free(addr);
        return (int*) 0;
      }
      return addr;  
    }
    
    So a value of 10 might be large enough, but I would try a larger value. It's possible that the malloc in cog_run is failing if your program is very large. This would cause the addr pointer to be zero, and the value of "1 + cogstart" would be written to location 0. If you only had 3 cogs running at the time this would produce a value of 4. Of course this means that the cog running clockCycle would be using a stack located near the beginning of RAM, which might actually sort of work.

    EDIT: cog_run should test whether the malloc succeeded, and return 0 if it didn't. It also seems odd that it stores the cog number + 1 at the first long of the stack and returns a pointer to the stack. You could check the return value from cog_run to see what it returns.
  • Daniel NagyDaniel Nagy Posts: 26
    edited 2015-03-04 11:58
    Hi,

    Thanks.
    I've tried higher values for stack, the behavior is the same.
    According to GCC reference, the second parameter is "Number of extra int variables for local variable declarations and call/return stack. This also needs to cover any local variable declarations in functions that your function calls, including library functions. Be liberal with extra stack space for prototyping, and if in doubt, 40 to whatever value you calculate."
    But you are right, sometimes it is referred as it should be a pointer to a memory area that could be used as a stack. I'm confused.
  • Daniel NagyDaniel Nagy Posts: 26
    edited 2015-03-04 12:03
    Hm, so far I was trying with LMM memory mode. By switching to CMM, the original code works as expected. Isn't it an issue with the compiler?
    (My code is quite small, by the way, 10kbyte or so is sent, and if I comment out the only print, it drops to 4k.) It's only an experiment with an SPI g sensor and an I2C pressure sensor. The print prints out the received data. Now I wanted to add timestamps.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-04 12:06
    Look at my second post. You may have missed it since we were posting at the same time.
  • Daniel NagyDaniel Nagy Posts: 26
    edited 2015-03-04 12:48
    I'm starting to absolutely not understand this.

    int id;
    id = cog_run(&clockCycle, 10);

    In LMM mode it returns 17456 (decimal)
    In CMM mode it is 10412 (decimal) and CLKFREQ returns 80M as it should.

    EDIT: Ah the number returned by cog_run are actually pointers, and not COG IDs (0-7) so the huge numbers are normal. The original problem I still don't understand. In CMM mode it works, in LMM it doesn't.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-04 13:44
    OK, so it looks like the malloc is not failing in LMM, so something else is writing to location 0. The heap grows from the end of the program toward higher addresses, so with an address of 17456 you should have plenty of heap space available. At this point I think you need to post a zipfile with all your code so we can see if something else is causing the problem.
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-03-04 18:54
    IIRC isn't clkfreq stored as at hub $0004. So CLKFREQ is returning the address, not the value ??? (I am not a C programmer so cannot help with this part)
  • Daniel NagyDaniel Nagy Posts: 26
    edited 2015-03-04 21:39
    CLKFREQ should return a value, not an address, and if the code compiled with CMM mode it does indeed. In LMM mode it returns 4.

    This evening I'll strip down the code, and if the problem exists with the bare minimum, I'll post it.

    Thanks the help so far.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-05 07:18
    There is a #define in propeller.h or cog.h that defines CLKFREQ to be _clkfreq in C. All C variables are prefixed with "_" when compiled to assembly, so in assembly this variable is __clkfreq. In spinboot.s, __clkfreq is declared as the first location in the file, and since this is linked to location 0, __clkfreq is the 32-bit integer at location 0.

    Location 0 contains the frequency of the clock. It should have a value of 80,000,000 if the frequency is 80 MHz. If it contains a value of 4 it must have been overwritten by user code. A NULL pointer is the usual primary suspect when location 0 gets overwritten by C code.
  • Daniel NagyDaniel Nagy Posts: 26
    edited 2015-03-05 08:17
    Dave,

    Thanks for your effort. Your logic is not only clear, but you are right as well. :)
    I found that my
    i2c_open(hp03_bus, PIN_HP03_SCK, PIN_HP03_SDA, 0);
    line is the guilty.

    And the root cause is that I don't understand what the first parameter should be.
    I've allocated a pointer like this:

    i2c* hp03_bus;

    and that is the variable you see as the first parameter.

    Now I understand i2c_open writes something where that pointer points, and that is an uninitialized zero, hence it overwrites addres 0;.

    This is clearly not how that first parameter should be used, I'll have to solve that.
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-03-05 10:30
    You should declare hp03_bus as "i2c hp03bus;", and then call i2c_open as "i2c_open(&hp03_bus, ". hp03_bus should either be a static or declared as a global variable. If you declare it on the stack it will go away when you return from the function. It would be OK to declare it on the stack in the main routine, however.

    So it appears that the Prop has an interesting way to detect a write using a null pointer. Just check if CLKFREQ changes from the expected value. A diagnostic could run in a separate cog that would spin in a loop checking CLKFREQ, and it could light an LED or print a message if it detects a bad value. Maybe there are other things that a diagnostic cog could look for.
  • Daniel NagyDaniel Nagy Posts: 26
    edited 2015-03-05 12:11
    Hello,

    Yes I've already figured out and had done the way you suggested.
    Thank you again for your help and thoughts!
    A diagnostic could run in a separate cog that would spin in a loop checking CLKFREQ, and it could light an LED or print a message if it detects a bad value.
    Or even better, slaps the dumb programmer's hand if trying to use a null pointer. :)
  • msrobotsmsrobots Posts: 3,709
    edited 2015-03-05 18:45
    Doable!

    Fly Squatter, rubber band and servo? @Erco's flame thrower?

    All just one diagnostic cog away!

    Enjoy!

    Mike
  • Daniel NagyDaniel Nagy Posts: 26
    edited 2015-03-06 23:31
    How could I set it to SOLVED?....
  • PublisonPublison Posts: 12,366
    edited 2015-03-09 08:43
    How could I set it to SOLVED?....

    Daniel,

    You can go to your first post, and go to the Advanced option. In the mean time, I have done it for you.
Sign In or Register to comment.