04 看门狗和Cache

注:本文学习朱有鹏老师课程的学习笔记

汇编写启动代码之关看门狗

1.什么是看门狗?
看门狗(watch dog timer 看门狗定时器)。
1、大家想象这样一个场景:家门口有一只狗,这个狗定时会饿(譬如说2小时一饿),狗饿了会胡乱咬死人。人进进出出要想保证安全,必须提前喂狗(必须在上次喂过后的2小时内喂狗才行)。如果超时没喂狗就会被咬死,如果提前喂狗没关系,但是本次喂狗时间就会从这里开始计算。
2、现实中因为一些外部因素,电子设备经常会跑飞或者死机(譬如极端炎热、极端寒冷、工业复杂场合)。在这种情况下我们希望设备自动复位而不需要人工干预(无人值守)。看门狗用来完成这个工作。看门狗其实是我们SoC内部的一个定时器(类似于闹钟,类似于门口的狗),定好时间之后看门狗定时器会去计时,时间到之前(狗饿了之前)必须去重新置位看门狗定时器(喂狗),如果没有喂狗则系统会被强制复位。
系统在正常工作时,系统软件会自己去喂狗,所以看门狗定时器不会复位。但是系统一旦故障跑飞,看门狗就
没人喂了,然后下一个周期就会自动复位,达到我们期望的效果。



2.分析硬件功能
物理特性上看门狗其实是个定时器(跟现实中的闹钟类似),硬件上就是SoC内部的一个内部外设。

3.找到关键性操作SFR(特殊功能寄存器)


WTCON(0xE2700000),其中bit5是看门狗开关:0代表关,1代表开


4.写代码关开门狗

#define WTCON	0xE2700000
.global _start					// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
	//关看门狗(向WTCON的bit5写入0即可)
	ldr r0, =WTCON
	ldr r1, =(0<<5)
	str r1, [r0]

5.总结210中看门狗特性(iROM中已经关看门狗)
为什么要关看门狗?
一般CPU设计,在CPU启动后看门狗默认是工作的(为什么默认不关闭而要工作?猜测是因为怕你的程序在启动代码前端就死机了或者跑飞了没人管),好处就是没有空当和漏洞,坏处就是在启动代码段我们不方便去喂狗(或者说懒得去喂狗)时看门狗会复位,所以为了偷懒我们就在启动代码的前段先去关闭看门狗,然后在后面系统启动起来之后再根据需要决定是否要打开看门狗(一旦打开就必须同时提供喂狗)。
在S5PV210内部的iROM代码(BL0)中,其实已经关过看门狗了。所以我们的启动代码实际上是不用去关的也没事的,也就是说上面写的关闭看门狗的代码运行后没有任何现象(没有现象就是正常现象)。
很多CPU内部是没有BL0的,因此也没人给你关看门狗,都要在启动代码前段自己写代码关看门狗,


汇编写启动代码之设置栈和调用C语言1

1.C语言运行时需要和栈的意义
“C语言运行时(runtime)”需要一定的条件(主要是栈),这些条件由汇编来提供。
1、C语言与栈的关系:C语言中的局部变量都是用栈来实现的。如果我们汇编部分没有给C部分预先设置合理合法的栈地址,那么C代码中定义的局部变量就会落空,整个程序就死掉了。
2、我们平时在编写单片机程序(譬如51单片机)或者编写应用程序时并没有去设置栈,但是C程序还是可以运行的。

原因:

@单片机中由硬件初始化时提供了一个默认可用的栈。

@在应用程序中我们编写的C程序其实并不是全部,编译器(gcc)在链接的时候会帮我们自动添加一个头,这个头就是一段引导我们的C程序能够执行的一段汇编实现的代码,

这个代码中就帮我们的C程序设置了栈及其他的运行时需要。

2.CPU模式和各种模式下的栈
@在ARM中37个寄存器中,每种模式下都有自己的独立的sp寄存器(r13)<注:sp-堆栈指针>,为什么这么设计?
如果各种模式都使用同一个sp,那么就意味着整个程序(OS内核程序、用户自己编写的应用程序)都是用一个栈的。
(你的应用程序如果一旦出错(譬如栈溢出),就会连累OS的栈也损坏,整个OS的程序就会崩溃。这样的OS设计是非常脆弱的,不合理的。)
所以各种模式要用不同的栈。我的OS系统内核使用自己的栈,每个应用程序也使用自己独立的栈,这样各是各的,一个损坏不会连累其他。我们现在要设置栈,我们先要找到自己的模式,然后设置自己的模式下的栈到合理合法的位置,即可。
注意:系统在复位后默认是进入SVC模式的

@我们如何访问SVC模式下的sp呢?很简单,先把模式设置为SVC,再直接操作sp.

注:因为我们复位后就已经是SVC模式了,所以直接设置sp即可。


3.查阅文档并设置栈指针至合法位置
栈必须是当前一段可用的内存(可用的意思是这个地方必须有被初始化过可以访问的内存,而且这个内存只会被我们用作栈,不会被其他程序征用)
当前CPU刚复位(刚启动),外部的DRAM尚未初始化,目前可用的内存只有内部的SRAM(因为它不需初始化即可使用)。

因此我们只能在SRAM中找一段内存来作为SVC的栈。

如图:


在ARM中,ATPCS(ARM关于程序应该怎么实现的一个规范)要求使用满减栈,所以不出意外都是用满减栈。
结合iROM_application_note中的memory map,可知SVC栈应该设置为0xD0037D80而不是0xD0037780

补充:

栈有4种:满减栈 满增栈 空减栈 空增栈

 进栈时出栈时
满减栈指针向下移动 -> 存数据出数据 -> 指针向上移动
满增栈 指针向上移动 -> 存数据出数据 -> 指针向下移动
空减栈存数据 -> 指针向下移动指针向上移动 -> 出数据
空增栈存数据 -> 指针向上移动指针向下移动 -> 出数据

4.接下来就可以实现汇编程序和C程序互相调用
#define WTCON	0xE2700000
#define SVC_STACK	0xd0037d80
.global _start					// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
	//关看门狗(向WTCON的bit5写入0即可)
	ldr r0,= WTCON
	ldr r1,= (0<<5)
	str r1,[r0]
	
	//设置SVC栈,实现汇编与C的相互调用
	ldr sp ,= SVC_STACK
	//接下来就可以直接调用C程序了
	bl Cfunction


汇编写启动代码之设置栈和调用C语言2


1.使用C语言来访问寄存器的语法
寄存器的地址类似于内存地址(IO与内存统一编址的),所以这里的问题是用C语言来读写寄存器,就是用C语言来读写内存地址。

用C语言来访问内存,就要用到指针;

格式:

unsigned int *p = (unsigned int *)0xE0200240;
*p = 0x11111111;
上面这两句等价于*((unsigned int *)0xE0200240) = 0x11111111;

2.

代码如下:

start.S文件:
#define WTCON		0xE2700000
#define SVC_STACK	0xD0037D80
.global _start					// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
	//关看门狗(向WTCON的bit5写入0即可)
	ldr r0,= WTCON
	ldr r1,= (0<<5)
	str r1,[r0]
	
	//设置SVC栈,实现汇编与C的相互调用
	ldr sp,= SVC_STACK
	//接下来就可以直接调用C程序了
	b czg_led

Makefile文件:
led.bin: start.o led.o
	arm-linux-ld -Ttext 0x0 -o led.elf $^		#代码段运行地址为0x0,将所有依赖文件链接为led.elf
	arm-linux-objcopy -O binary led.elf led.bin #将led.elf复制一份为led.bin文件
	arm-linux-objdump -D led.elf > led_elf.dis	#将led.elf文件转换为.dis反汇编文件
	gcc mkv210_image.c -o mkx210
	./mkx210 led.bin 210.bin
	
%.o : %.S
	arm-linux-gcc -o $@ $< -c -nostdlib

%.o : %.c
	arm-linux-gcc -o $@ $< -c -nostdlib

clean:
	rm *.o *.elf *.bin *.dis mkx210 -f

led.c文件:
#define GPJ0CON	0xE0200240
#define GPJ0DAT 0xE0200244
#define rGPJ0CON	*((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT	*((volatile unsigned int *)GPJ0DAT)

void delay(void){
	unsigned int i = 900000;
	while(i--);
}

void czg_led(void){
	rGPJ0CON = (0x111<<12);	// 将三个引脚设置成out模式
	while(1){
	rGPJ0DAT = (0<<3)|(1<<4)|(1<<5);
	delay();
	rGPJ0DAT = (1<<3)|(0<<4)|(1<<5);
	delay();
	rGPJ0DAT = (1<<3)|(1<<4)|(0<<5);
	delay();
	}
}

3.关于volatile解释:

http://blog.youkuaiyun.com/czg13548930186/article/details/52454032

汇编写指令代码之开iCache

1.什么是cache,有什么用?
1、cache是一种内存,叫高速缓存
从容量来说:CPU < 寄存器 < cache < DDR

从速度来说:CPU > 寄存器 > cache > DDR


cache的存在,是因为寄存器和DDR之间速度差异太大,DDR的速度远不能满足寄存器的需要(不能满足CPU的需要,所以没有cache会拉低整个系统的整体速度)
2、整个系统中CPU的供应链由:寄存器+cache+DDR+硬盘/flash四阶组成,这是综合考虑了性能、成本后得到的妥协的结果。

3、210内部有32KB icache和32KB dcache。

icache用来缓存指令的;

dcache是用来缓存数据的。

4、cache的意义:

指令平时是放在硬盘/flash中的,运行时读取到DDR中,再从DDR中读给寄存器,再由寄存器送给CPU。但是DDR的速度和寄存器(代表的就是CPU)相差太大,如果CPU运行完一句再去DDR读取下一句,那么CPU的速度完全就被DDR给拖慢了。

解决方案就是:icache.
icache工作时,会把我们CPU正在运行的指令的旁边几句指令事先给读取到icache中(CPU设计有一个基本原理:代码执行时,下一句执行当前一句代码旁边代码的可能性要大很多)。当下一句CPU要指令时候,cache首先检查自己事先准备的缓存指令中有没这句,如果有就直接拿给CPU,如果没有则需要从DDR中重新去读取拿给CPU,并同时做一系列的
动作:清缓存、重新缓存。

2.iROM中BL0对cache的操作
首先,icache的一切动作都是自动的,不需人为干预。我们所需要做的就是打开/关闭icache。
齐次,在210的iROM中BL0已经打开了icache。所以之前看到的现象都是icache打开时的现象。

3.查阅ARM手册中CP15寄存器的相关部分

1、ARM处理器中CP15协处理器的寄存器,如下图所示:

2、由上图可知,CP15包括了16个寄存器,其中C1寄存器是控制寄存器,主要用于:

(1)禁止/使能MMU以及其它与存储系统有关的功能

(2)配置存储系统以及ARM处理器相关的工作

 C1寄存器的位定义,如下图所示:

注:由上图可知,bit12用于开关iCache,其中0代表关闭iCache,1代表开启iCache。


3.代码

start.S文件:
#define WTCON		0xE2700000
#define SVC_STACK	0xD0037D80
.global _start					// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
	//关看门狗(向WTCON的bit5写入0即可)
	ldr r0,= WTCON
	ldr r1,= (0<<5)
	str r1,[r0]
	
	//设置SVC栈,实现汇编与C的相互调用
	ldr sp,= SVC_STACK
	
	//开关icache
	mrc p15,0,r0,c1,c0,0    //读出cp15的c1到r0中
    bic r0, r0, #(1<<12)    //bit12 清0 关icache
    orr r0, r0, #(1<<12)    //bit12 置1 开icache
    mcr p15,0,r0,c1,c0,0    //将r0写入cp15中的c1中
	
	//接下来就可以直接调用C程序了
	b czg_led


4.实验验证

 直接使用BL0中对icache的操作手动关icache手动开icache
现象(结论)速度不变,且和手动开icache相同,证明BL0初始化了icache速度明显变慢速度不变
### nRF52832 Watchdog Timer Configuration and Usage The nRF52832 chip incorporates a watchdog timer that functions similarly to the one on the NRF51822, with some specific configurations and functionalities. The watchdog timer is essentially a countdown counter which triggers a `TIMEOUT` event when it reaches zero[^1]. To start this timer, developers use the `START` task provided by the Nordic SDK or Zephyr RTOS APIs. If no other clock source at 32.768kHz is available upon starting the watchdog timer, the system automatically enables an internal 32.768kHz RC oscillator specifically for the watchdog operation. By default, the watchdog continues running even during periods where the CPU enters sleep mode or gets paused via debugging tools. However, there exists flexibility within its setup allowing users to configure whether the watchdog should pause under these conditions through appropriate register settings. For calculating the timeout period of the watchdog timer in seconds, apply the formula `(CRV + 1) / 32768`, wherein CRV represents the compare value set inside relevant registers controlling the watchdog behavior. In terms of practical implementation using C code within projects based around platforms like Zephyr OS, initializing timeouts related structures can be done as shown below: ```c #define initialize_timeouts() do { \ sys_dlist_init(&_timeout_q); \ } while ((0)) ``` This macro initializes a doubly linked list used internally for managing various types of timers including potentially those associated with watchdog operations depending on how they are integrated into application logic[^2]. When configuring preemptive behaviors concerning scheduling policies alongside watchdog mechanisms, consider reviewing parts of your kernel's scheduler handling such as demonstrated here from sched.c file snippet: ```c // Pseudo-code representation extracted from sched.c contextually similar lines. void update_cache(){ ... should_preempt(); // Determines if higher priority threads need immediate attention over current execution flow. } ``` Such interactions between different components highlight considerations necessary when integrating complex features involving both timing constraints imposed by hardware watchdog requirements along with software-level concurrency management aspects found across embedded systems development environments[^3].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

种瓜大爷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值