OS实现过程之Xtensa DC_D_233L
来源:互联网 发布:ubuntu安装firefox 编辑:程序博客网 时间:2024/05/22 23:53
写这篇文章的目的,主要是给那些新手看的,以满足他们的好奇心,让他们知道写OS会经历哪些过程。至于老手,都已经会写OS了,本文就没啥吸引力了。大家瞅瞅就行 。
1. 背景
有现成的bootloader、OS、芯片驱动、产品,CPU是Xtensa DC_D_233L。我们有其它部分的源代码,但OS是其它公司开发的,源码没有。
2. 设计目的
在DC_D_233L 上运行的多线程操作系统。当前已有bootloader、CPU内部模块的驱动、官方GDB(不是linux中的GDB)等。
3. 实现步骤
(1)了解OS的结构;(2)查阅xtensa官方手册,了解芯片工作机制;(3)分析xtensa官方手册,提取出与OS相关的内容;(4)在纸上或电子文档中写下OS运行过程;(5)检查第(4)步的结果是否可行;;(6)编写OS代码,在电路板或仿真器上运行。
4.了解OS的结构
多线程OS的结构如下:(1)内存管理;(2)中断和异常处理;(3)线程调度;(4)系统调用;(5)文件系统(暂不实现);(6)兼容现有OS接口;多线程和多进程非常不同,多进程的文件系统是一大难点。多线程可就简单好多好多了,不用实现文件系统、代码常驻在内存中,这些都省好多事情。
5 查阅xtensa官方手册,了解芯片工作机制
芯片工作机制,包含如下内容:(1)CPU寻址过程;(2)发生中断时,硬件对中断的处理过程;上述2项内容是必须要了解的。第(1)步牵涉到CPU寻址硬件的工作流程,它会使用很多硬件结构和寄存器,它们可能会成为单个线程正常工作的一部分。第(2)步是为了线程切换做准备,因为10ms定时中断一定会使用这种机制进行线程切换。xtensa官方文档很多,为了实现上述2个目的,需要做如下2步:(1)浏览多个官方文档,找出与上述目的相关的所有文档;(2)对第(1)步的文档进行重要性排序,先看最重要的文档;本人第(1)步唯一找到的文档是微处理器编程手册,紧接着就开始看了。坦白说,这份文档真不错,感觉是手把手教人如何写OS,就连代码设计的背后考虑,它都进行了详细说明。文档信息整理,这是本人现在面对的问题。这个编程手册才200多页,瞅瞅ARM的那些手册,动辄上千页,整理信息是很容易迷路和失去耐心的。抱着目的看文档,能有效解决这个问题。写OS是个艰苦的活,整理文档信息是第一步,是不可避免的。
6. 分析xtensa官方手册,提取出与OS相关的内容
这次的目的性非常强。首先是对总结内容的排序:(1)先做内存管理的总结。内存管理肯定排在线程调度、系统调用前面,但是如果内存访问时没有触发异常或引发中断,则中断和异常处理是不必要的;内存管理却是时时都要用的。因此先做内存管理的总结,直到其完成为止。(2)再做中断和异常处理的总结。中断和异常处理肯定排在系统调用前面,另外在中断里可以进行线程调度(如10ms定时中断),因此再做中断和异常处理的总结。(3)再做线程调度的总结。它肯定要排在系统调用的前面。(4)再做系统调用的总结。(5)兼容现有OS接口:这个暂时不考虑,等到写出了OS,再做兼容不迟。在总结过程中,我先在纸质笔记本上记录心中所想(纸张比电子文档更让人能思考),后来发现图表在纸质笔记本上写着不方便(经常删除内容),因此后来改用电子文档做总结思考。但是,总是在思考成熟后,比如在提出一个疑问并获得答案后,才会在电子文档上做记录。例如,内存寻址步骤是什么?每步做了什么事?内存寻址的参与对象有哪些?这步做下来,A3纸用了29页。
7. 设计的细化
7.1 设计目标
分2步实现我们的最终目标:(1)写出最小的、能正常工作的OS,OS越简单越好,效率高低无所谓;(2)在第一步的基础上,逐步扩充功能;采用上述策略的原因是:(1)一次性实现所有功能,难度大;(2)某些功能并非必需;(3)一次性实现所有功能,不利于程序调试;(4)一次性实现所有功能,不能增加软件实现者的信心(非常重要!);上述方案可以让我们一开始,就集中注意力在验证文档总结是否正确、核心模块如何实现上,非核心模块先不做。这种方式可以让我们在初期就发现设计缺陷。当然了,我们的设计仍是大而全的,只是先实现核心模块而已。
7.2 内存管理的设计
内存管理的目标:(1)不启用MMU(也就是说,不使用虚拟地址到物理地址的转换硬件---非常重大的设计决策);(2)可以随时分配内存、随时释放内存;(3)内存大小的改变不影响程序的改动;因为上述目标,使得内存管理蜕变为一个算法。
7.3 中断和异常处理的设计
(1)必须能够处理各种中断;(2)支持高频率的中断处理(此为通信芯片,物理层中断非常频繁);(3)支持硬件中断嵌套,不支持软件中断嵌套(这个决策与芯片有关);
7.4 线程调度的设计
(1)线程可以创建无数个;(2)线程可以手动退出,也可以自动退出;(3)提供线程间通信方法;(4)线程调度算法易于修改;(6)所有线程都是privileged权限;(7)线程的优先级可以在创建线程时设置(此为通信芯片,某些线程优先级很高);(8)线程调度接口简单;
7.5 系统调用的设计
系统调用函数分为2类: (1)线程切换类; (2)非线程切换类。之所以分类,是因为中断处理函数开放给用户编写,用户可能会在其中胡乱调用系统调用函数。所以对每个系统调用函数分类,使得在中断中只能调用非线程切换类函数。
8. 实现策略
8.1 最终目标
#include <stdio.h>#include "OS_API.h"void *taskA(void *arg){ u32 i; while (1) { for (i = 0; i < 2000000; i++); schedule(); }}void *taskB(void *arg){ u32 i; while (1) { for (i = 0; i < 2000000; i++); schedule(); }}int main(){ u32 i; /* 1: register main() as a task, and put it to task chain, this has * been done statically */ /* 2: */ mem_init(); /* 3: */ interrupt_init(); /* 4: must be done after memory init */ sched_init(); /* 5: start testing */ thread_create(taskA, NULL); thread_create(taskB, NULL); while (1) { for (i = 0; i < 2000000; i++); schedule(); } return 1;}
上述代码只有创建线程和线程调度,没有使用内存分配算法和开启中断。这符合先前的简化目标,因为我们仍处于验证设计是否可行的阶段,此时不应把代码弄复杂。
8.2 系统初始化
分类 内容 初始化顺序 PC寄存器,SP寄存器 21全局设置——> PS寄存器 14 WINDOWBASE寄存器 11windows机制 WINDOWSTART寄存器 12 a0寄存器,a1寄存器 13中断机制 INTENABLE寄存器 1 INTERRUPT寄存器 2 ICOUNTLEVEL寄存器 6debug异常机制 IBREAKENABLE寄存器 5 DBREAKCn寄存器 4zero-overhead机制 LCOUNT寄存器 3时间计算 CCOUNT寄存器 7扩展L32R模式 LITBASE寄存器 8 PTEVADDR寄存器 15MMU机制 RASID寄存器 16 ITLBCFG寄存器,DTLBCFG寄存器 17协处理器 CPENABLE寄存器 9浮点寄存器 FSR寄存器,FCR寄存器 10MMU机制 指令cache,数据cache 18 TLB entries或CACHEATTR寄存器 19 其它寄存器 其它硬件结构ROM解压 从ROM中解压到内存中 20调用第1个C函数 调用main() 22
8.3 内存管理初始化
存储访问中的参与对象:
ITLB,DTLB组ITLBCFG,DTLBCFG寄存器RASID寄存器PTEVADDR寄存器(存放页表的基地址)cache结构EXCVADDR寄存器
在CPU上电启动后,应做如下初始化工作:(没有列出先后顺序)
(1)ITLB组和DTLB组中,除了way 6,其余的way都置为invalid。(2)设置ITLBCFG寄存器和DTLBCFG寄存器,以设置way4, way5, way6的页表大小。(3)设置RASID寄存器的值为0x04030201。(4)清空cache中所有内容。(5)不设置PTEVADDR寄存器。因为此时位于ring 0,寻址可以使用way6,故不会发生硬件自动填充TLB的情况。
在main()运行之前:
(1)定义1个数组avail_memory[内存字节大小/ (1024 × 4 ×8)],用每个成员的每个bit表示1个4KB的内存;bit = 0表示该4KB没有被使用,bit = 1表示该4KB正在被使用;(2)将数组avail_memory[]的每个成员都置为0;(3)定义存储分配函数为: void *emalloc(u32 size); // 从主内存中分配size个字节的存储区 void efree(void *addr); // 从主内存中释放以addr为起始地址的存储区【注】之所以叫emalloc(),efree(),是不想与malloc()和free()混淆。
在mem_init()内部:
(1)将avail_memory[]数组清零;
8.4中断初始化
main()运行之前:
(1)定义1个数组:void (*interrupt_handler)[32];
interrupt_init()内部:
(1)将interrupt_handler[]数组全部设置为NULL;(2)将10ms定时中断的中断优先级设置为最低(为了嵌套中断能工作更顺利);
8.5 线程调度初始化
main()运行之前:
(1)将main()定义成1个线程;这步一定要将main()定义为1个线程,原因是这样的:main()一定会创建新线程,当新线程被中断或主动进行线程切换时,如果main()不是一个线程(从而它不在全局的线程链表中),则线程调度时,永远不会选择并切换回main()。
sched_init()内部:
空;
thread_create()内部:
(1)使用phy_malloc()为每个新线程分配PCB;(2)对该新PCB进行结构方面的初始化;(3)对该新PCB进行栈上内容的初始化;
9. 实现总结
(1)查阅并总结文档非常耗时,占整个时间的2/3;编码及测试只占1/3;(2)尽量看官方文档,官方文档比网络上的二手资料准确;(3)先写核心模块,再逐步扩充OS功能。OS功能众多,难道要一次性全部写完?对本人而言,一次性写完难度太高了,根本完成不了。所以在设计阶段,就应经想好实现模块的先后顺序。初期实现目标代码,当然是越简单越好(否则就没信心了),无所谓效率,能进行线程创建、线程切换就行,此时不要求线程退出(线程就是无限循环)。这个阶段伴随着设计方案的验证、设计方案的修改、芯片理解的校正等,逐步实现线程切换。一旦实现线程切换,则可以加上内存分配、互斥量、条件变量等,再加上中断和异常处理、定时器,形成可以工作的OS;(4)xtensa GDB可以进行软件层的线程切换(Keil也可以),它是个非常强大的程序调试工具;
0 0
- OS实现过程之Xtensa DC_D_233L
- Documentation/xtensa/mmu.txt
- 【软件工程】 之 实现过程
- 嵌入式os的实现一之任务切换的实现
- uboot之eth实现过程
- WebRTC之appRTC实现过程
- chromium OS编译过程
- Z/OS 启动过程
- MAC OS开机过程
- 嵌入式单地址空间OS中实现动态加载的过程
- 实现OS中BOOTLOADER过程,并进入保护模式的代码
- 简单OS开发前奏(三)操作系统装载过程及BootSector的汇编语言实现
- 简单OS开发前奏(三)操作系统装载过程及BootSector的汇编语言实现
- Mac OS X 启动过程
- Mac OS X 系统启动过程
- matplotlib Mac OS 安装过程
- 分析Ezgui之SendMessage实现过程
- android学习之ECService的实现过程
- SpringMVC从Controller中获取json数据
- 一个包含Jersey库的简单Web Service以及一个发送Json数据的Java客户端
- 分割平面与空间公式
- 在Sqlite中通过Replace来实现插入和更新
- tableview的分割线 扩充
- OS实现过程之Xtensa DC_D_233L
- GFI Archiver™ 2015 SR1
- 文本文件与二进制文件区别(转)
- jdbc操作mysql数据库
- CentOS 下 beanstalkd & supervisord & laravel 后台队列环境安装配置
- How to setup memcache, in XAMPP mac osx lion
- TCP粘包问题解决
- LOCAL_JNI_SHARED_LIBRARIES
- Keil uVesion4 高级查找功能的使用方法