龙芯开源社区

 找回密码
 注册新用户(newuser)
楼主: alexzyy

2f 的频率调节 --降频测试+超频

[复制链接]
 楼主| 发表于 2008-8-6 21:46:15 | 显示全部楼层
foxsen版主,如果用内部的时钟源,是不是即使变频也对节能意义不大?如果是这样那确实没什么用处了,平白无故让CPU变慢
今天刚试了一下,把time.c里的符号EXPORT出来,重新编译后终于可以看到一些感兴趣的变量了,把cycles_per_jiff改掉确实会让时钟快慢可调的,这样在写CR80的同时把这个变量也同步修改应该可以修正对时钟的影响,具体有没有误差还没分析
 楼主| 发表于 2008-8-6 21:53:45 | 显示全部楼层
加了个内核定时器去测量CPU的空闲时间,至此基本完成一个测试模型,基本思想,CPU利用率大于90%的时候就让它跳到最大速度,空闲大于某个阈值时自动一点点减速,不过别指望这个东西能省多少电,因为CPU本身也耗不了多少电,大概对笔记本意义更实在一些。
我测试的内核是重编过的,把time.c里的一些变量给 EXPORT出来了,否则把调整系统时间的函数去掉也应该能工作,不过变频的时候系统时钟就会乱掉了。
下面是几天工作的结果,降频测试至此结束,估计很快龙梦会有官方的电源管理,本本电池需要嘛,咱也不多花时间了。
顺便说一下,测试的结果,CPU大部分时间能工作在CR80=2或者3上,放电影也不会全速。
测试是用的模块的形式,编译成模块然后 insmod ls2f_freq.ko,在/var/log/kern.log里可以看到变频的记录

/* proof of concept code, ugly organized, for your reference
* experimental CPU freq adjust, without any even implied warranty, use it at your own risk
* Jul 31,2008
*
* v0.01 basic test, change CPU frequency by write to CR80
* v0.02 adjusted system clock
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/timer.h>
#include <asm/ict/tc-godson.h>
#include <asm/mc146818-time.h>
#include <asm/time.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/kernel_stat.h>
#include <asm/time.h>
#include <asm/bootinfo.h>

//how frequently to monitor the CPU load, 250 about 1sec,50 about 0.2sec
#define MY_EXPIRE 150
#define PCR80_ADDR 0x1fe00180UL

static unsigned char read_speed(void);
void speed_up( void );
void speed_down( void );

//variable from arch\mips\kernel\time.c
extern  unsigned long cycles_per_jiffy;
extern  unsigned int expirelo;
extern unsigned int mips_hpt_frequency;

//variables to calculate the cpu load,calculation freq 5/sec
cputime64_t user, nice, system, idle, iowait, irq, softirq, steal, all, old_all,old_idle;
int avg_cpu_idle=0; //assume cpu fully utilized initially,avg_cpu_load=(avg_cpu_load*4+current_cpu_load)/5
struct resource *m_region;
void*   cr80_addr;
unsigned long ori_cycles_per_jiff;
struct timer_list my_timer;
//thresholds to slow the CPU down, assume CPU freq ~ CR80 is: 200:1, 300:2,...., 800:7
//below looks like a very aggressive target ..., may impact the GUI user experience
int idle_thres[8]={50,40,35,30,30,30,30,30};
//sys time adj cycles_per_jiff
unsigned long adj_jiff[8];



//calculate current CPU utilization
//range from 0 ~ 100, stands for IDLE percentage
int cal_cpu_load( void )
{
        int cur_idle_pct;
        user = kstat_cpu(0).cpustat.user;
        nice = kstat_cpu(0).cpustat.nice;
        system = kstat_cpu(0).cpustat.system;
        idle = kstat_cpu(0).cpustat.idle;
        iowait = kstat_cpu(0).cpustat.iowait;
        irq = kstat_cpu(0).cpustat.irq;
        softirq = kstat_cpu(0).cpustat.softirq;
        steal = kstat_cpu(0).cpustat.steal;

        all = user + nice + system + idle + iowait + irq + softirq + steal;
        cur_idle_pct = (idle-old_idle)*100/(all-old_all);

        old_all = all;
        old_idle = idle;

        //smooth it
        avg_cpu_idle = (avg_cpu_idle * 19 + cur_idle_pct)/20;
        //printk("current cpu idle %d , avg cpu idle %d\n",cur_idle_pct,avg_cpu_idle);

        return cur_idle_pct;
}

//monitor the CPU idle, and adj the CPU freq as needed
void monitor_adj_cpu_freq(int idle_pct,int cur_speed)
{
        if(idle_pct < 5){   //looks CPU is getting busy, speed up !
                if(cur_speed<7){
                    printk("cur speed %d,cur idl %d %%, avg idle %d%%,ls2f going to speed up!\n",cur_speed,idle_pct,avg_cpu_idle);
                    speed_up();
                }
        }
        else if( avg_cpu_idle > idle_thres[cur_speed]){
                if(cur_speed>1) {
if(cur_speed>1) {
                    printk("cur speed %d,cur idl %d %%, avg idle %d%%,ls2f going to speed down!\n",cur_speed,idle_pct,avg_cpu_idle);
                    speed_down();
                }
        }
}

//timer handler, monitor CPU load and do accord freq adjust
void do_adjust(unsigned long arg)
{
        struct timer_list * p_timer;
        int idle_pct;
        p_timer = (struct timer_list*)arg;
        //printk("I'm timer p_timer->expire %lu\n",p_timer->expires);
        idle_pct = cal_cpu_load();
        monitor_adj_cpu_freq(idle_pct,read_speed());
        p_timer->expires += MY_EXPIRE ;
        add_timer(p_timer);
}
//change the system clock freq by adjusting LATCH value in PIT 8253/8254
int adj_systime(int tgt_speed)
{
        //printk("mipshpt %d\n",mips_hpt_frequency);
        //printk("current cycles_per_jiffy %ld,expirelo %d \n",cycles_per_jiffy,expirelo);
        //disable_irq(23);
        //printk("target jiffy %lu\n",adj_jiff[ tgt_speed ]);
        cycles_per_jiffy = adj_jiff[ tgt_speed ];
        return 0;
}

static unsigned char read_speed(void)
{
    unsigned char v;
    v = readb( cr80_addr);
    v &=0x07;
    return v;
    //return readb ( cr80_addr );
}

void write_speed(unsigned char speed)
{
        unsigned char v;
        //check input value first, shall range from 1:7
        if(speed < 1 || speed>7){
                printk("accepts range only from 1 to 7 , you specified %d\n",speed);
        }
        else{
//read current value and set bit 0:2
            v = readb( cr80_addr );
            //clear bit 0:2
            //printk("current speed %x\n",v);
            v >>= 3;
            v <<= 3;
            v |= speed;
            //printk("target speed %x\n",v);
            writeb(v, cr80_addr);
            //adj sys time
            adj_systime( v&0x07 );
            //printk("speed after adjust %x\n",read_speed());
        }

}
//if the CPU gets busy, put it to work at full speed
void speed_up( void )
{
        write_speed( 7 );
        avg_cpu_idle = 0;
}
//if the CPU is idle, lower the freq gradually
void speed_down( void)
{
        unsigned char v;
        v = read_speed();
        if(v<=1)
                return;
        write_speed(v-1);
}
//
static int f_init(void)
{
        int i;
        printk(KERN_ALERT"freq starting\n");
        m_region = request_mem_region( PCR80_ADDR, 4, "loongson");
        if( m_region == NULL){
                printk("io memory request failed\n");
                return -1;
        }
        else{
                printk("io memory request succeeded\n");
        }
        cr80_addr = ioremap( PCR80_ADDR, 2);
        printk("CPU speed %x\n",read_speed());
init_timer(&my_timer);
        my_timer.expires = MY_EXPIRE + jiffies;
        my_timer.function = do_adjust;
        my_timer.data = (unsigned long)&my_timer;

        //keep the original value
        ori_cycles_per_jiff = cycles_per_jiffy;

        all = user = nice = system = idle = iowait = irq = softirq = steal = cputime64_zero;
        cal_cpu_load();

        //calculate sys time adj factors
        for(i=0;i<8;i++){
            adj_jiff = ori_cycles_per_jiff *(i+1)/(8);
        }

        add_timer(&my_timer);
        return 0;
}

static void f_exit(void)
{
        printk(KERN_ALERT"freq exiting\n");
        //reset to full speed
        write_speed(7);
        if(m_region != NULL)
                release_mem_region(PCR80_ADDR,4);
        if(cr80_addr != NULL)
                iounmap( cr80_addr );
        del_timer( &my_timer );
        cycles_per_jiffy = ori_cycles_per_jiff; //restore systime
}

MODULE_LICENSE("GPL");
module_init(f_init);
module_exit(f_exit);


[ 本帖最后由 alexzyy 于 2008-8-7 22:03 编辑 ]

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册新用户(newuser)

x
 楼主| 发表于 2008-8-7 22:06:29 | 显示全部楼层
以后有时间,看看能不能超频, ,尽管到现在为止还没头绪
发表于 2008-8-8 09:05:53 | 显示全部楼层
盒子真正使用时候变频作用不大,因为使用时候感觉盒子反应还是挺慢的。
特别是上网的时候。
--平时待机的时候可能有些意义,但是全速才5w的cpu,cpu再怎么降频也减少不了6w吧?

真正提升性能的是超频。
 楼主| 发表于 2008-8-10 21:48:46 | 显示全部楼层
不懂硬件的人来做这事还真是费不少事,好不容易明白了CS5536是2F的南桥芯片,里面有一个集成的SMBUS控制器,参考了http://hengch.blog.163.com/blog/static/10780067200841952313414/
然后想办法找到这几个寄存器的IO基址,应该是4cf0,
然后再找I2C协议,对我来说已经是太复杂了,感谢GOOGLE,又找到一个源码
http://qa.coreboot.org/docs/doxy ... mbus_8c-source.html
简单组装一下,测试将时钟频率调到80MHZ,这样龙芯应该运行在1G
insmod加载,还没等看结果呢,过了一分钟,2F罢工了,按RESET键无法启动,只好断电源再启
原理很简单,就是通过I2C总线读写时钟芯片的05号寄存器
代码和模块贴出来,请高手看看,是硬件无法运行在1G,还是软件应该改进
 楼主| 发表于 2008-8-10 21:59:51 | 显示全部楼层
可能会有不少错误,有熟悉的修改吧

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册新用户(newuser)

x
发表于 2008-8-10 23:16:21 | 显示全部楼层
只想看到降频成功的消息,超频的事不太关心,比较危险。超速驾驶……
我的6002已经和我的2。4晒羊装乌班图一样的功能了,除了IM……不过发现没有QQ也不会怎么样。单位已经禁掉QQ了,所以家里不能上QQ也没啥感觉了。目前我的6002能连数码相机,用我的U盘,可以下电影听歌。能上我经常去的网站,不会在要去的网站上有东东不能看了,发现盒子的软件一步一步越来越好了。每一个进步,让我都非常高兴。现在机上装了KTORRENT和AMULE,那台X86都懒得用了。
发表于 2008-8-11 23:27:38 | 显示全部楼层
原帖由 alexzyy 于 2008-8-10 21:48 发表
不懂硬件的人来做这事还真是费不少事,好不容易明白了CS5536是2F的南桥芯片,里面有一个集成的SMBUS控制器,参考了http://hengch.blog.163.com/blog/static/10780067200841952313414/
然后想办法找到这几个 ...


能稳定一分钟, 应该软件没有问题了。 应该是硬件的事。
发表于 2008-8-12 01:19:59 | 显示全部楼层
热肯定是从cpu冒出来的,它身上背一个散热器就是证明,在6003上,好像cpu比显示驱动芯片还热,改变cpu的频率,可以减小cpu上的发热。显示驱动芯片上也背了一个散热器,也是节能对象,当工作在节能状态下,关闭显示驱动芯片上的处理器将减少发热,而被显示的缓冲数据放在显示动态存储器里,应当尽可能地保护,因为我们知道存储器发热不太大。 动态内存模块发热不大,可以一直让它处于工作状态,保存工作现场数据。 笔记本上还要关掉发光器件。
     如果cpu自身可以让驱动它的外部时钟源频率降为0,系统心脏可能自己就不能再跳起来了,除非有外部的作用,重新将时钟源信号送入cpu。 而如果cpu仅仅让驱动它的外部时钟源降低输出的频率,这时cpu没有完全静止,或许还可以慢慢地跑,以响应外部事件。 能在不睡死的范围内逐级缓变 cpu 的主频时钟,需要一定的试验。换言之,在变频的情况下,还能维持cpu指令流的处理就很理想了,系统设计员应该尝试这方面的试验。
      动态内存DDR的刷新时钟源应该是与cpu主频时钟源独立的;通常在嵌入式系统上静态存储器SRAM就不受cpu主频影响。 cpu通过i2c通道设定了DDR工作频率后就不应该再费心了。   但是主频变动后,数据是否能同样可靠地从内存经过总线到达cpu cache,可能就得试验了。在静态内存的情况下是不会担心太多的。
      如果您有条件贴一个小温度传感器在cpu的散热器上,或者干脆打开盒子,用手感触,以知道调节各种频率前后的温度,就可以间接而可靠地看到调频为节能带来的效果了。
     进行cpu调频的试验时,最好能参考外部时钟源,总线,内存等的电路图,驱动路径和相关资料,硬件设施是基础,不然成了摸象的游戏。
     至于cpu内部的HPT(High precision timer)可能是一个高分辨率的可编程计数器/定时器,它的时钟驱动源如果同cpu主频相关的话,调节HPT可能就靠不住了。准确的时基信号可能还是得从外部独立的RTC取得。
      在x86 linux 启动时,好像有个CPUFreq governer 的模块被启动运行。

[ 本帖最后由 strongchina 于 2008-8-13 17:12 编辑 ]
发表于 2008-8-12 08:40:21 | 显示全部楼层
目前就是不知道外部有什么可用的RTC源,怎么使用。

本版积分规则

小黑屋|手机版|Archiver|Lemote Inc.  

GMT+8, 2019-9-18 20:01 , Processed in 0.201352 second(s), 18 queries .

快速回复 返回顶部 返回列表