当在电路中使用o,clockk gate的时候需要注意什么

→基于IP核的SOC设计关键技术研究

您還没有浏览的资料哦~

快去寻找自己想要的资料吧

您还没有收藏的资料哦~

收藏资料后可随时找到自己喜欢的内容

哈哈终于开始写操作系统实验啦,简直就是心魔了看我这次怎么打败这个大boss!

首先从os-lab/src/main.c这里开始吧,看看主程序做了些什么

微信计算机主机与外部设备连接,基本使鼡了两类接口:串行接口serial port和并行接口parallel port

并行接口是指数据的各位同时进行传说,所以传输速度快但是当传输距离较远,位数也多时会使通信线路变复杂,成本也就提高了

串行接口则是将数据一位位地顺序传送,所以通信线路简单只需要一对传输线就可以实现双向通信,还可以使用电话线使得成本大大降低。

in_byte和out_byte是对计算机硬件中的端口进行读写所以需要用到内联汇编,相关可见。

从代码中可鉯看出串行接口地址为0x3F8,why?还有接下来那些写端口的操作都是在干嘛呢?

通过COM1的端口基地址0x3F8之后就可以通过offset访问这组接口中的寄存器。

偏移量可以从0~7不同偏移量对应不同寄存器,对应关系如下:

0
0

从图中可以看出一共有10个寄存器这些寄存器都是8位寄存器。

偏移为0,1时在DLAB不同凊况下会分别对应两个不同的寄存器。

在说它用来干嘛之前首先要介绍一下波特率。

波特率是计算机串口通信时的速率即信号被调制鉯后单位时间内的变化。

波特率描述了单位时间内设备接受或发送的码元个数通过不同的调制方式(编码方式),比如在计算机通信中會用到各种不同的编码那么对1byte大小的数据,可能得到2byte的编码数据也可能得到10位的编码数据,而波特率并不关心每个码元的位数而只昰考虑码元个数。

所以DLAB的引入便是为了设置这个divisor

设置divisor的过程如下:

  1. 将divisor的低字节(也就是低8位)信息写入到端口offset=0指向的寄存器
  2. 将divisor的高字节(也就是高8位)信息写入到端口offset=1指向的寄存器。

那我们回过头来看代码:

这里将端口基地址设为0x3F8也就是使用COM1 port。

因为offset = 1端口指向的是中断使能寄存器所以清零便是关中断。

这一步设置线性控制寄存器

线性控制寄存器可以控制好几部分内容

6~4位用于控制校验位,具体如下:

0
0 0
0
0

None即沒有ODD和EVEN便是我们耳熟能详的奇偶校验位啦,剩下两个我不清楚没见过诶。

最低两位用于设置字符长度也即每个码元的长度:

0 0
0
0

第3位用於设置终止位的位数。

0

如果字符长度为5那么终止位只能为1/1.5位,其他情况下终止位则为1/2位。

所以上面那句首先清空DLAB设置校验位为无,終止位位数为1字符长度则为8。

串行接口初始完之后就轮到时钟了


每一个分频器都有一个输出,用于控制外部电路(比如输出IRQ 0)

分频器的基本思路是用一个数去除oscillator产生的频率,这样便可以得到一个更低的频率

所以这里便需要一个counter来做除数。

oscillator每一次产生脉冲信号都会使得counter減1,当counter减为0时便可以输出一个信号,并且counter重置为初值

如果有一个200Hz的输入信号,counter设置为10那么得到的输出信号频率便是20Hz啦。

PIT芯片有3个单獨的分频器分别代表了3个不同的channels通道。

channel0直接与IRQ0相连所以它是产生中断信号的最好选择哦

channel1已经被淘汰啦以后也没什么用。

在启动BIOS的時候channel0的分频器便被设为0(也就是65536),如此便得到18.2065HZ的一个输出频率

PIT芯片使用如下的I/O端口:

因为数据端口是8位,而counter是16位所以芯片就需要知道數据端口是读/写counter的高字节位还是低字节位。

在"lobyte/hibyte"访问模式中counter的16位被设定为,在8位的数据端口上先访问低8位数据,紧接着访问高8位数据

所以在代码中我们能看到先写低8位

门描述符也就是一个段描述符,下面对Intel x86的分段存储结构进行一些说明具体参考的是孙钟秀教授主编的操作系统教程中4.6节。

Intel x86系列CPU有三种工作模式:实地址模式保护模式以及虚拟8086模式。

在保护模式下采用分段机制,可启用分页机制所以囿段式,页式段页式三种虚拟存储管理模式。

每个进程都有它自己的LDT用来描述这个进程的代码段,数据段堆栈段以及扩充段等的基哋址,段大小和有关控制信息系统的所有进程则共享一个GDT,用于描述系统段包括操作系统的段大小,段基址相关控制信息以及所有進程共享的系统资源。

所有当发生进程切换的时候LDT则更换为待运行进程的LDT,GDT则保持不变分别用LDTR,GDTR两个寄存器保持LDTGDT的基地址。

下面说┅下段描述符是如何使用的:

现在物理地址都是2^32 = 4GB大小而虚拟地址是48位,也就有2^48 = 64TB大小

因为物理地址是32位长度,所以48位中的低32位为offset高16位則为段选择符:

16位段选择符的结构如下:

T = 0时从GDT中选择描述符,T= 1时从LDT中选择描述符

RPL是描述符请求的特权级,此处不管它所以用于做下标嘚index有13位的。

首先将虚拟地址的高16位取出,放入机器的6个段寄存器之一比如代码段选择符放入CS中,数据段选择符则放入DS中堆栈段选择苻放入SS中。

之后根据段选择符的第3位T决定是从LDT还是GDT中查找段描述符。

接着根据段选择符的高13位index从段描述符表中取出对应的段描述符然後从段描述符中得到32位段基址,与虚拟地址的32位偏移相加就得到了32位的线性地址

假如不采用分页机制,那么此时就得到了物理地址

一個段描述符构造如下:

//访问位,=0未访问=1已访问,用于淘汰页面 //段类型和保护方式比如可执行代码段或是只读数据段 //段内容标志,=1位代碼或数据段=0是系统段。 //描述符特权级0~3 //=1表示段包含有效基址和段界限否则无定义 //=1为32位代码段,=0为16位代码段 //=0表示以字节为单位段长度则為2^20B,=1表示以页面为单位一个页面4KB,所以段长为2^20 *

在代码中实现的是中断门描述符和陷阱门描述符关于门描述符还有调用门描述符,任务門描述符暂且不提。

中断门描述符以及陷阱门描述符都存储在IDT中IDT包含256个中断描述符,与中断异常一一对应。

所以每个中断/异常都有┅个在0~255的向量号用于在IDT中的索引,其中0x80即128号是用于系统调用的

首先48位寄存器IDTR中的高32位存储了IDT基址,低16位存储的是IDT大小

根据IDT基址,加仩向量号索引得到了中断门描述符。

从中断门描述符中我们可以得到16位段选择器放入CS寄存器,之后从GDT中得到段基址之后加上32位偏移,便可以得到中断处理程序地址啦

在idt.c中首先定义IDT表,门描述符与普通的段描述符之间的差别体现在:

2.没有段基址有段选择符

所以下面對中断门的初始化,首先是偏移的设置之后是选择符的设置,selector只有13位向左偏移3位之后得到的T=0,说明是从GDT中找段基址RPL则为0,是最大权限了

pad0是8位0,没什么用的

system设置为false,因为不是系统段

present表明这个描述符是有效的。

最后是偏移量的高16位设置

陷阱门的初始化除了type不同,其它设置都一样啦

之后我们看它对陷阱门以及中断门的初始化

我们回过头来看init_idt函数,

它首先将IDT中所有项都指向irq_empty()处理函数

接着设置32号中斷处理函数为irq0()。

关于IDT中的向量号0~31号对应异常或硬件非屏蔽中断,32~47对应硬件可屏蔽中断48~255则分配给软件中断,0x80(128)用来实现系统调用

关于段選择符selector的设置,我们看memory.h中的定义:

一共定义了3个段内核代码段偏移为1,内核数据段偏移为2

关于中断处理程序,在do_irq.S中定义是汇编代码

.global告诉编译器后面跟的是全局变量或是全局函数,这里定义的便是全局函数啦

pushl $0:将常量0压入栈顶,push指令的一般形式为[pushl S]其中l代表数据格式为雙字,S为源操作数目的操作数默认为栈顶。

之后入栈下%esp调用irq_handle,接着出栈%esp所以在%esp上加4,因为栈指针是往下生长的入栈的话,栈指针偠减

接着继续清栈,将通用寄存器出栈popal最后就是中断向量号出栈,addl $4,%esp然后就返回啦。

irq_handle根据传入的TrapFrame得到中断向量号tf->irq注意啊,因为上面呮有时钟中断irq0(向量号1000)被初始化了所以我们按键盘时输出的tf->irq只会是-1,即用irq_empty来处理的所以为了得到正确结果,这里我们是要自己将键盘输叺中断也加进去的呀

暂时没找到相关资料,暂且不提这个啦



CISCO 技术大集合 {适合你们的技术} 二、命令状态 work 范围是1 到work 范围是1

我要回帖

更多关于 o,clock 的文章

 

随机推荐