init
|
@ -0,0 +1,173 @@
|
|||
### 什么是编译器?
|
||||
个人认为,编译器是从一个指令集到另一个指令集的**映射工具**。为实现此功能,任何编译器都需要做这3件事,1是**分解**原指令集,2是**分析**原指令集,即将原指令集转化为等价于目标指令集的结构,显然这个结构也是等价于目标指令集的,3是将所得的结构**重组**成目标指令集。以v9.js为例,它也做的是这三件事:1构建词法分析器,2构建语法分析器,3构建虚拟机及指令集。
|
||||
|
||||
### 四个主要的函数
|
||||
* next:获取下一个标记,用于词法分析
|
||||
所有支持的标记都被放入一个枚举类型中,词法分析器的框架,换行符,宏定义,标识符与符号表,数字,字符串,注释,lookahead,关键字与内置函数
|
||||
* decl:语法分析,使用递归下降的方法
|
||||
BNF语法,左递归的消除
|
||||
变量定义:EBNF表示,解析变量的定义
|
||||
函数定义:函数定义的解析,解析参数,函数体的解析
|
||||
* expr:解析一个表达式
|
||||
stmt函数:解析一个语句(IF,WHILE,RETURN,BREAK,CONTINUE,FOR,DO,SWITCH,CASE,ASM,VA_START,DEFAULT,GOTO,ID,其它语句)
|
||||
运算符的优先级:逆波兰式,一元运算符,二元运算符
|
||||
|
||||
### 一个简化的计算机
|
||||
* 内存:text(代码段),data(数据段),stack(栈)
|
||||
* 寄存器:pc(程序计数器),sp(指针寄存器),bp(基址寄存器),ax(通用寄存器)
|
||||
* 指令集:一个字符型数组ops[]和一个枚举类型
|
||||
1. IMM(将立即数放入ax),LC(将字符地址放入ax),LI(将整数地址放入ax),SC(将ax中的数据放入栈中),SI(将ax中的数据放入栈中)
|
||||
2. PSHA(将ax的值入栈)
|
||||
3. JMP(无条件跳转)
|
||||
4. JZ/JNZ(ax为零/不为零跳转)
|
||||
5. CALL调用,RET返回,ENT子函数调用,ADJ清空栈中子函数的参数,LEV返回,LEA参数调用
|
||||
6. 运算符指令,要注意第一个参数在栈顶,第二个参数在ax中
|
||||
7. 内置函数:exit,open,close,read,printf,malloc,memset,memcmp
|
||||
|
||||
### 全局变量
|
||||
* file字符型指针,用于指向预处理包含文件的文件名或源文件的文件名
|
||||
* cmd字符型指针,用于指向命令的名字
|
||||
* pos字符型指针,用于指示源文件中字符的位置
|
||||
* incl字符型指针,用于指示预处理包含文件的路径
|
||||
|
||||
* tk整型变量,用于记录当前标记
|
||||
* ts、ip整型变量,分别指代码段和代码指针
|
||||
* gs、data整型变量,指数据段、当前偏移
|
||||
* bss整型变量,bss偏移
|
||||
* loc整型变量,本地帧偏移
|
||||
* line整型变量,用于记录行号
|
||||
* ival整型变量,当前标记的整数值
|
||||
* errors整型变量,错误的数量
|
||||
* verbose整型变量,c编译器的-v选项,输出详细信息
|
||||
* debug整型变量,c编译器的-s选项,打印源和生成代码
|
||||
* ffun整型变量,末解决的函数计数器
|
||||
* va、vp整形变量,分别指变量池和变量指针
|
||||
* e整型变量的指针,是表达式树的指针
|
||||
* pdata整型变量的指针,数据段修补指针
|
||||
* pbss整型变量的指针,BSS段修补指针
|
||||
|
||||
* fval双精度型变量,当前标记的双精度型变量
|
||||
|
||||
* ty无符号整型变量,当前解析的子表达式类型
|
||||
* rt无符号整型变量,当前解析的函数返回类型
|
||||
* bigend无符号整型变量,大端机器
|
||||
* info_fd文件句柄
|
||||
* id:ident_t结构体的指针,当前解析标识符
|
||||
|
||||
### 标记与汇编码
|
||||
- 标记
|
||||

|
||||
- 汇编码
|
||||

|
||||
|
||||
### 静态变量
|
||||
* iline静态整型变量,预处理包含文件所在的行
|
||||
* ifile静态字符型指针变量,可能代表预处理包含文件的句柄
|
||||
* ipos静态字符型指针变量,意指预处理包含文件在文件中的位置
|
||||
* iname静态字符型数组,保存着预处理包含文件的路径的字符串
|
||||
* brk静态整型变量,
|
||||
|
||||
### main函数
|
||||
* 输入整型变量argc是变量个数,argv是字符型指针变量的数组,输出整型变量
|
||||
* 数据结构:
|
||||
hdr结构体:看起来像是在模拟硬盘
|
||||
outfile字符型指针:输出文件的文件名
|
||||
tmain:ident_t结构体的指针,
|
||||
* 工作流程:获取编译参数,为目标代码申请内存空间,将源文件读入内存,将源代码转换为目标代码,将目标代码写入文件中。
|
||||
|
||||
### next函数
|
||||
* 详见src/xvcc.c文件
|
||||
* 无输入,无输出,操作的均为静态变量和公共变量。把源码输出为标记流。
|
||||
* 所有标记都放在一个枚举型变量中,注意标记是有顺序的
|
||||
* 私有变量:p,hm
|
||||
* b整型变量,表明一个字符串或文件的长度
|
||||
* 处理逻辑:
|
||||
* 如果是空格,水平制表'\t',垂直制表'\v',回车'\r',换页'\f',则跳出本循环,读下一个字符。
|
||||
* 如果是换行'\n',则行数加1,跳出本循环,读下一个字符。
|
||||
* 如果是'#',则判断是否是预处理语句"#include",依次读取预处理包含文件。
|
||||
* 如果是字母、下划线或美元符号"$",
|
||||
* 如果是数字,
|
||||
* 如果是斜杠“/”,可能是除号或注释。
|
||||
* 如果是斜杠"\"或",判断是哪种转义字符。
|
||||
* 如果是“=”,可能是Eq或Assign。
|
||||
* 如果是运算符,则为tk赋值相应的标记。
|
||||
* 如果是其它符号,则为tk赋值相应的标记。
|
||||
```
|
||||
变量id(Id), Ptr, Notf, Nzf, Fun, FFun, 函数调用Fcall, 符号标记(Label, FLabel),
|
||||
Cic, Cuc, Cis, Cus, Addaf, Subaf, Mulaf, Dvua,
|
||||
Divaf, Mdua, Srua,
|
||||
* 关键字标记(括号内为标记)
|
||||
asm(Asm) auto(Auto) break(Break) case(Case) char(Char) const
|
||||
continue(Continue) default(Default) do(Do) double(Double)
|
||||
else(Else) enum(Enum) extern float(Float) for(For) goto(Goto)
|
||||
if(If) int(Int) long(Long) register return(Return) short(Short)
|
||||
signed sizeof(Sizeof) static(Static) struct(Struct)
|
||||
switch(Switch) typedef(TypeDef) union(Union) unsigned(Unsigned)
|
||||
void(Void) volatile while(While)
|
||||
va_list(Va_list), va_start(Va_start), va_arg(Va_arg),
|
||||
整数Num, 浮点数Numf, 多个点Dots,
|
||||
汇编指令Lea, 汇编指令Leag,汇编指令Cid, 汇编指令Cud, 汇编指令Cdi, 汇编指令Cdu,
|
||||
汇编指令Eqf, 汇编指令Nef, 汇编指令Ltu, 汇编指令Ltf, 汇编指令Geu, 汇编指令Gef, 汇编指令Sru, 汇编指令Addf, 汇编指令Subf, 汇编指令Mulf,汇编指令Dvu, 汇编指令Divf, 汇编指令Mdu,
|
||||
```
|
||||
```
|
||||
* 运算符标记,从上到下优先级降低
|
||||
()Paren []Brak ->Arrow .Dot
|
||||
!Not ~ ++Inc --Dec + - * (type) sizeof
|
||||
*Mul /Div %Mod
|
||||
+Add -Sub
|
||||
<<Shl >>Shr
|
||||
<Lt <=Le >Gt >=Ge
|
||||
==Eq !=Ne
|
||||
&And
|
||||
^Xor
|
||||
|Or
|
||||
&&Lan
|
||||
||Lor
|
||||
?:Cond
|
||||
=Assign +=Adda -=Suba *=Mula /=Diva %=Moda &=Anda ^=Xora |=Ora <<=Shla >>=Shra
|
||||
,Comma
|
||||
```
|
||||
|
||||
### stmt函数
|
||||
* 无输入,无输出,操作的均为静态变量和公共变量。把语句解析成汇编代码。
|
||||
* 私有变量:c, d, et, cmin, cmax
|
||||
a看起来像是语句的某个跳转入口
|
||||
b看起来像是语句的某个跳转入口
|
||||
es用于存储表达树的指针
|
||||
* 处理逻辑:
|
||||
* IF语句:判断表达式的值,为真则顺序执行然后跳转到语句末尾,为假则跳转到else语句。
|
||||
* WHILE语句:判断表达式的值,为真则顺序执行然后跳转到语句前端,为假则跳到语句末尾。
|
||||
* RETURE语句:使用LEV指令返回函数。
|
||||
* Break语句:
|
||||
|
||||
### expr函数
|
||||
* 输入整型,无返回值。把表达式解析成汇编代码。
|
||||
* 如果是数字,标记为Num或Numf。
|
||||
* 如果是字符串,标记为Leag。
|
||||
* 如果是Id,
|
||||
* 如果是Va_arg,
|
||||
* 如果是Sizeof,
|
||||
|
||||
### decl函数
|
||||
- 输入整型,无返回值。
|
||||
|
||||
### info_函数
|
||||
* info_open输入文件名的指针,无返回值。打开文件,将文件句柄赋值给公共变量,为ip赋值。
|
||||
* mapfile输入文件名的指针,输出字符型指针,将文件内容载入到内存中。
|
||||
|
||||
### 指令发射器(introduction emitter)
|
||||
* em,emi,emj,eml,emg,emf,patch
|
||||
|
||||
### 分析器
|
||||
* next, imm, immf, tsize, tinc, talign, basetype, type, decl, member
|
||||
* dline是个分析器,将行中的某个位置到行末的距离输出到句柄为info_fd的文件中。
|
||||
* skip输入整型变量c,无输出,作用是跳过标记c。
|
||||
|
||||
### 表达式分析
|
||||
* node, nodc, mul, add, flot, ind, addr, assign, trim, cast, expr,
|
||||
|
||||
### 表达式生成
|
||||
* lmod, smod, lbf, opf, opaf, lbi, lb, opt, opi, op, opa, rv, stmt
|
||||
* testnot输入表达式树指针和一个整数,返回一个整数。
|
||||
* test输入表达式树的指针和一个整数,返回一个整数。
|
||||
* opt输入表达式树指针,无返回值。
|
|
@ -0,0 +1,85 @@
|
|||
### 实验pa1a
|
||||
|
||||
#### 源代码的结构
|
||||
|
||||
1. Lexer.l用以生成Lexer.java文件,作用是把decaf文件的源代码转化为Token的形式,供Parser.java使用,完成的是词法分析的功能。是用正则表达式来实现此功能的。
|
||||
2. Parser.y用以生成Parser.java文件,作用是把从Lexer.java得到的Token转化为语法树,完成的是语法分析的功能。是用文法规则实现此功能的。
|
||||
3. 抽象类BaseLexer是作为类Lexer的父类而出现的。
|
||||
4. 抽象类BasePaser是作为类Paser的父类而出现的。
|
||||
5. 类SemValue是在为Token赋值。
|
||||
6. 抽象类Tree:代表抽象语法树的结点。我理解抽象语法树描述的是源代码的**逻辑结构**,我**猜测**每一个叶子结点都是一个**基本数据类型**,每一个非叶子结点都是对基本数据类型的**操作**。抽象类Tree为不同类型的结点设置了约80种属性,比如属性NEWARRAY代表创建新数组,RETURN代表返回状态,IF代表条件语句。这些属性实际上是互不相同的整数。抽象类Tree内设计了近40个它的子类,
|
||||
|
||||
#### 跟踪注释的处理过程
|
||||
1. 依据正则表达式("//"[^\r\n]*{NEWLINE})将注释匹配为S_COMMENT
|
||||
2. 依据注释的规则 { /* Just ignore */ },什么都不做,注释处理完毕
|
||||
|
||||
#### 跟踪关键字的处理过程
|
||||
1. 是直接匹配和关键字对应的字符串,关键字对应的规则均为{ return keyword(Parser.XXX); }
|
||||
2. Parser.XXX的XXX是在Parser.y文件中定义的标记(token),通过观察Parser.java文件可见它实际上是个序号唯一的数字
|
||||
3. keyword方法的作用是为Parser对象的yylval属性赋值,并返回Parser.XXX的值。
|
||||
4. yylval是一个SemValue对象,是为它的loc和code属性赋值,code就是Parser.XXX的值,loc是一个Location对象。
|
||||
5. loc有两个属性,lin代表行号,col代表列号。
|
||||
6. 到这里我觉得有点奇怪,目前编译器应该是输入decaf源文件,输出输出类似Testcase/S1/result/*.result之类的文件,但从以上分析看不出有输出什么到文件。我想是不是应该看看总控函数public static void main做了些什么。
|
||||
7. 通过grep命令找到main函数在Driver.java文件中。main函数只有4条指令,它创建了一个Driver对象,对Driver对象的option属性赋值,调用init方法,调用compile方法。
|
||||
8. option属性是一个Option对象,在赋值的时候是通过读取命令参数来指定Option对象的属性srcFileName,dstFileName,input,output。
|
||||
9. init方法创建了Lexer对象,Parser对象,DecafError对象。
|
||||
10. compile方法首先使用parser.parseFile方法解析输入并执行指示项,然后使用checkPoint方法找到解析错误的地方,最后使用条件判断做相应的处理。
|
||||
11. 通过总控函数知道了大概的过程,但还是很迷惑,因为对象之间的关系也很复杂。
|
||||
12. 应该怎么样继续理解编译器里这些对象呢?从整体来看其实有三个对象:decaf源文件,编译器,decaf目标文件。编译器里分了这么多对象,其实目标只有一个,就是把源文件转换成目标文件。我想从源文件和目标文件这个角度来理解编译器的工作原理是不是更容易一点呢?
|
||||
13. 源文件在Driver对象的option属性里,option属性本身也是Opiton对象,源文件具体对应着的是srcFileName属性。
|
||||
14. srcFileName被转化成了InputStream对象input,我猜测就是把源文件载入内存,并把内存中的文件抽象成字节流。
|
||||
15. input传递给了Lexer对象lexer,lexer和parser绑定对应关系。
|
||||
16. parser.yyparse方法进行了具体的分析工作。依据Yacc的说明文档,yyparse又调用了yylex进行词法分析的工作,得到标记(tokens)。如果一切正常,yyparse会返回0,否则返回1。
|
||||
|
||||
|
||||
#### decaf-PA1A的处理过程是什么样的?
|
||||
|
||||
1. decaf的总控函数在类Driver.java里,理解了Driver.java里的main函数也就理解了decaf的执行逻辑。
|
||||
2. 首先会打开源文件,创建目标文件。这部分工作是由Option类的构造函数来完成的。最重要的两个属性是input和output。
|
||||
3. 具体的分析工作是由Parser类的parseFile方法来完成的。它的作用是生成一颗语法分析树。是采用的自底向上的LALR(1)分析方法。
|
||||
4. Parser类的parseFile方法就是yyparse()方法,yyparse()方法是由Parser.y自动生成的语法分析工具。
|
||||
5. yyparse()会调用yylex()方法来获得标记,yylex()方法是由Lexer.l自动生成的词法分析工具。yylex()方法每次只返回一个标记。
|
||||
6. 把生成的语法分析树写入目标文件中,这是由Tree类的printTo()方法来完成的。至此,decaf-PA1A的处理过程结束。
|
||||
|
||||
#### 相同名称的变量出现在源代码不同文件中,它们的含义有什么不同吗?
|
||||
|
||||
1. Lexer.l中的标记(NEWLINE、INTEGER等)只是正则表达式的记号,它并未出现在lexer.java中,由此可见lexer.l中的标记对源代码的其它部分无任何影响。lexer.l中用到的另一种标记Parser.XXX只是类Parser的属性。
|
||||
2. Parser.y中的标记(由%token定义)是文法分析里的终结符,是在文法规则里用的。在Parser.java中体现为类的属性。而在Parser.y中出现的Tree.XXX虽然名字和Parser.y中的标记相同,但它们的数据类型、值和代表的意义都是不一样的。
|
||||
3. Tree.java中的属性(public static final int TOPLEVEL等)虽然有一部分名字和Parser.y中的标记相同,但表达的意义不同,它表示的是抽象语法树里的某种结点。
|
||||
4. SemValue.java中即用到了Tree.XXX也用到了Parser.XXX。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
- install:
|
||||
|
||||
`sudo apt install flex bison`
|
||||
|
||||
- 作用:
|
||||
|
||||
flex: 生成一个确定有穷自动机(DFA),使用正则表达式描述正则文法。
|
||||
|
||||
bison:使用BNF范式描述上下文无关文法,生成的应该是下推自动机。
|
||||
|
||||
- 结构:flex和bison的程序均由三部分构成
|
||||
|
||||
```
|
||||
声明
|
||||
%%
|
||||
规则
|
||||
%%
|
||||
C代码
|
||||
```
|
||||
|
||||
> %{和%}之间的代码直接照抄到生成的C文件中
|
||||
|
||||
- flex的语法:
|
||||
|
||||
- 正则表达式
|
||||
- 消除二义性:最大匹配,最早匹配
|
||||
- yyin: 读取输入的文件句柄(输入管理的第一个层次)
|
||||
- YY_BUFFER_STATE:创建并使用输入缓冲区(输入管理的第二个层次)
|
||||
- YY_INPUT:重定义输入到当前缓冲区的宏(输入管理的第三个层次)
|
||||
- yyout:所有没有被匹配的输入都会复制到yyout,可使用`%option nodefault`禁用它
|
||||
- flex的库文件: -lfl,定义了默认的main例程和yywrap,然而基本上所有的flex程序都使用`%option noyywwap`和自己的主例程,所以它们并不需要flex库。
|
||||
- yytext: 字符串
|
||||
- yylex(): 读取一个部分输入,返回下一个记号。记号由两部分组成:记号的编号和记号的值(yyval)。
|
||||
- yyrestart(f):使词法分析器读取标准输入输出文件f。
|
||||
- yyterminate():立即从词法分析器返回。
|
||||
- %x IFILE:把IFILE定义为起始状态。
|
||||
- INITIAL:flex本身定义的状态
|
||||
- %s:声明包含的起始状态
|
||||
- BEGIN:切换到另外一个起始状态
|
||||
- `<<EOF>>`:匹配输入文件的结束
|
||||
|
||||
- bison的语法:
|
||||
|
||||
- %token: 记号名称。
|
||||
- 起始符号在第一条规则的左边。
|
|
@ -0,0 +1,75 @@
|
|||
- 形式语言与自动机本质上是一种数学模型。
|
||||
- 形式语言和自动机的理论基础
|
||||
- Chomsky对自然语言的研究
|
||||
- 巴科斯-诺尔范式(BNF)
|
||||
- Kleene在研究神经细胞时建立的自动机模型
|
||||
- 先修知识:集合、关系、图论
|
||||
- 分类
|
||||
- 0型 短语文法 图灵机(Turing machine)
|
||||
- 1型 上下文有关文法 线性有界自动机LBA(Linear Bounded Automaton)
|
||||
- 2型 上下文无关文法 下推自动机PDA(PushDown Automaton)
|
||||
- 3型 右线性文法 正则文法 有限自动机(Finite-state machine)
|
||||
|
||||
|
||||
1. 形式语言概念
|
||||
1. 字母表,字(**字母表**中的符号组成**字**)
|
||||
2. 字和字母表的运算(字的**连接**运算,字母表的**幂**是字的集合,**星闭包**是所有字的集体,**正闭包**是除空字外所有字的集体)
|
||||
3. 语言(**语言**是闭包的子集)
|
||||
4. 语言的运算(**并**,**连接**,**闭包**,**幂**)
|
||||
2. 正规语言
|
||||
1. 基本概念
|
||||
|
||||
- **正规表达式**就是满足并,连接和闭包这三种运算的表达式。
|
||||
- **正规语言**就是满足正规表达式的语言。
|
||||
|
||||
2. 有限自动机(计算能力等价于正规表达式)
|
||||
|
||||
0. 有限自动机=**状态集**+**输入符号集**+**转移函数**+**开始状态**+**终态集**
|
||||
|
||||
1. 确定有限自动机DFA(deterministic finite automaton)
|
||||
|
||||
DFA就是一些**状态**和这些状态转换的**规则**。DFA的语言就是满足DFA的所有**字的集合**。
|
||||
|
||||
2. 非确定有限自动机NFA
|
||||
|
||||
NFA的状态转换不唯一。
|
||||
|
||||
3. 带ε-转移的非确定有限自动机
|
||||
|
||||
4. 有限自动机的确定化
|
||||
|
||||
从NFA或ε-NFA构造等价的DFA的算法(**子集构造法**)。
|
||||
|
||||
5. DFA的最小化
|
||||
|
||||
使DFA的状态数最小的算法(**填表算法**)
|
||||
|
||||
6. 从正规表达式到有限自动机
|
||||
|
||||
从正规表达式构造ε-NFA的算法
|
||||
3. 上下文无关文法
|
||||
1. 上下文无关文法的基本概念
|
||||
|
||||
上下文无关文法=**终结符集**+**非终结符集**+**开始符号**+**产生式**
|
||||
|
||||
2. 归约与推导
|
||||
|
||||
**归约**是自下而上的过程,是将产生式的右部替换为产生式的左部。
|
||||
|
||||
**推导**是自上而下的过程,是将产生式的左部替换为产生式的右部。
|
||||
|
||||
3. 上下文无关语言
|
||||
|
||||
4. 句型,句子与分析树
|
||||
|
||||
**句型**是一个可以由开始符号推导出的单词序列
|
||||
|
||||
**句子**是全部由终结符组成的句型
|
||||
|
||||
把从开始符号到句子的推导过程用有向树的形式表现出来就叫**分析树**。
|
||||
|
||||
5. 归约、推导与分析树之间的关系
|
||||
|
||||
6. 文法的二义性
|
||||
|
||||
如果一个文法存在两棵及以上的分析树,则称它为**二义的**。如果存在一棵及以下的分析树,则称它为**无二义的**。
|
|
@ -0,0 +1,21 @@
|
|||
**关于编译原理课的文档**
|
||||
|
||||
formal-language.md: 以我的理解表述阅读材料《形式语言与自动机》
|
||||
|
||||
compiler-example.md: 一个简化的C语言编译器的说明文档。
|
||||
|
||||
decaf-pa1a.md: 关于课后实验pa1a的文档。
|
||||
|
||||
lecture1.md:简介
|
||||
|
||||
lecture2.md:实验引导一
|
||||
|
||||
lecture3-lexical.md: 词法分析
|
||||
|
||||
lecture4_5.md:语法分析
|
||||
|
||||
lecture6.md:符号表
|
||||
|
||||
lecture7.md:实验引导2
|
||||
|
||||
lecture8.md:语义计算基础
|
|
@ -0,0 +1,25 @@
|
|||
* 编译程序做的工作是把一种语言翻译成另一种语言。
|
||||
|
||||
个人认为,编译器面对的是两个事:**语言**和**自动机**。编译原理讲解的也是两个事:**设计**语言和**实现**自动机。**词法分析**讲解的是设计正则语言和有限自动机。**语法分析**讲解的是设计上下文无关语言和下推自动机。 **语法制导**和**中间代码生成**讲解的也是设计上下文无关方法和下推自动机。上下文无关语言从结构上看应该是树形结构,所以用在语法分析阶段用来推导抽象语法树,在中间代码阶段生成中间代码。正则语言从结构上看是线性结构,所以在词法分析阶段用来分析同样是线性结构的源代码。
|
||||
|
||||
* 编译过程分为分析和综合两个阶段。
|
||||
|
||||
分析就是把线性结构转换成非线性结构,结构上变复杂了,但对于处理复杂性来说反而变简单了。综合就是把非线性结构再转换成线性结构。对应到编译程序,前端(Front End)完成的是分析的工作,后端(Back End)完成的是综合的工作。中端(Middle End)完成的是中间代码的生成与优化,即有分析也有综合。
|
||||
|
||||
* 源代码的七十二变。
|
||||
|
||||
源代码刚开始是字符流,经过**词法分析**变成了单词流,经过**语法分析**变成了抽象语法树,经过**语义分析**变成了中间代码,中间代码经过**中间代码的生成与优化**变成了另一种中间代码,经过**目标代码生成**变成目标代码,目标代码还可以进行**目标代码优化**变成优化的目标代码。
|
||||
|
||||
* 符号表和错误处理
|
||||
|
||||
1. 符号表管理和错误处理遍布于编译的整个过程
|
||||
2. 符号表看起来就是抽象语法树上添加了属性,每个结点都是一个符号表。
|
||||
3. 错误处理要报告出错信息,而且期望能报告尽可能多的错误,而不是在找到一处错误就停止。
|
||||
|
||||
* 抽象语法树
|
||||
|
||||
源代码的逻辑结构是树形结构,把这个树形结构表示出来就是抽象语法树。
|
||||
|
||||
* 形形色色的编译程序
|
||||
|
||||
我更喜欢把它们都叫做编译程序,因为它们都完成的是把一种语言翻译成另一种语言的工作。它们是:解释器(Interpreter),预处理程序(Preprocessor),编译器(Compiler),汇编器(Assembler),装入和链接程序(Loader and Linker),调试程序(Debugger)。
|
|
@ -0,0 +1,16 @@
|
|||
* 词法分析是把字符流翻译成单词流
|
||||
|
||||
所谓单词(Lexemes)就是对编译有意义的部分,对编译没有意义的部分(注释、空格、换行等)被直接丢弃掉了。单词由型(Token)和值(Attribute)两部分组成。
|
||||
|
||||
* 单词是怎么被识别出来的?
|
||||
|
||||
1. EBNF
|
||||
2. 状态转换图
|
||||
3. 正规表达式
|
||||
4. 有限状态自动机
|
||||
|
||||
* 自动构造词法分析程序
|
||||
|
||||
词法分析在逻辑上是简单的,在实现上是复杂的,让简单回归简单,这就是**自动构造**存在的意义。
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
* 语法分析是把单词流翻译成抽象语法树
|
||||
|
||||
有两种构造抽象语法树的方法:从根结点到叶子结点构造(**自顶向下方法**),从叶子结点到根结点构造(**自底向上方法)**。语法分析总是从左到右依次扫描单词流,这就是LL或LR文法里第一个字母L的含义。LL文法第二个L的意思是最左推导,LR文法第二个R的意思是最右推导。
|
||||
|
||||
* 产生式、推导和归约
|
||||
|
||||
**产生式**是非终结符号的某种构造方法。**推导**是将产生式的左部替换为产生式的右部。**归约**是将产生式的右部替换为产生式的左部。**最左推导**总是选择每个句型的最左非终结符进行推导。**最右推导**总是选择每个句型最右的非终结符号进行推导。
|
||||
|
||||
* 自顶向下的语法分析
|
||||
|
||||
* 两个问题:一选择哪个终结符是不确定的,二选择哪个产生式是不确定的。可以通过最左推导的方法消除第一个不确定性。
|
||||
* 消除左递归和提取左公因子
|
||||
* LL(1)分析的1代表向前查看1个单词,为什么要向前看?因为要消除第二种不确定性。
|
||||
* LL(1)文法要求对于任意非终结符,其所有推导结果均不能相同。LL(1)文法是使LL(1)分析消除不确定性的文法。
|
||||
* 怎么样才能使所有推导结果都不相同呢?预测集合不相交即可。
|
||||
* first集是所有可能符号串的第一个终结符的集合。first集合的计算方法。
|
||||
* follow集合是方法符号后面跟的可能的终结符的集合(不包括空字符)。follow集合的计算方法。
|
||||
* LL(1)分析之递归下降
|
||||
* LL(1)分析之表驱动
|
||||
* 错误处理
|
||||
|
||||
* 自底向上的语法分析
|
||||
|
||||
* 两个问题:一选择哪个产生式是不确定的,二匹配哪个位置上的子串是不确定的。
|
||||
* 移进归约分析,两种类型的冲突,表驱动方法
|
||||
* LR分析
|
|
@ -0,0 +1,2 @@
|
|||
* 表达式树(expression tree):以数据形式表示语言代码,是一种抽象语法树或数据结构。表达式树与Lambda表达式相关联。表达式树也是一种简单的抽象语法树(AST)。
|
||||
表达式树的树叶是操作数(operand),结点是运算符(operator)。
|
|
@ -0,0 +1,17 @@
|
|||
正则表示法:
|
||||
^表示行首匹配或补集:
|
||||
^word //行首匹配
|
||||
\[^xyz] //匹配不包含xyz的任意多个字符
|
||||
|
||||
word$ //行尾匹配
|
||||
* //匹配前面的表达式零次以上
|
||||
+ //匹配前面的表达式一次以上
|
||||
? //匹配前面的表达式零次或一次
|
||||
. //任意一个字符,但不包括\n和\r
|
||||
\ //转意特殊字符串
|
||||
[xyz] //匹配xyz中意一个字符
|
||||
{} //一个数字表示匹配次数,两个数字表示匹配范围,带有名字时表示以此名字命名的模式
|
||||
| //选择操作符
|
||||
"..." //引号中的字符将基于字面意思
|
||||
() //把一系列的正则表达式组成一个新的正则表达式
|
||||
/ //尾部上下文
|
|
@ -0,0 +1 @@
|
|||
[堆](heap)
|
|
@ -0,0 +1,19 @@
|
|||
参考:[51cto博客_算法](http://363883844.blog.51cto.com/2977542/1544700)
|
||||
#### 什么是堆
|
||||
* 堆的关系满足:(k<sub>i</sub><=k<sub>2i</sub> ,k<sub>i</sub><=k<sub>2i+1</sub>)或者(k<sub>i</sub>>=k<sub>2i</sub> ,k<sub>i</sub>>=k<sub>2i+1</sub>)
|
||||
* 堆的分类:顺序存储堆,链式存储堆(*个人认为顺序存储堆才是真正的堆,链式存储堆不就是二叉树么*)
|
||||
链式存储堆 = 左式堆 + 斜堆 + 二项堆
|
||||
* 堆总是一颗完全二叉树,完全二叉树是其结点编号能与其所在的满二叉树完全对应的二叉树,满二叉树是每一层上的结点数都是该层的最大结点数的二叉树。
|
||||
|
||||
#### 顺序存储堆
|
||||
* 顺序存储堆从物理结构上看是数组,从逻辑结构上看是完全二叉树。
|
||||
|
||||
#### 左式堆
|
||||
* 每一个结点其左子树零路径长不小于右子树
|
||||
* 零路径长:结点到一个叶子结点的最短路径长
|
||||
|
||||
#### 斜堆
|
||||
* 与左式堆类似,不同之处在于合并后不管左右子树零路径大小都必须交换左右子树
|
||||
|
||||
#### 二项堆
|
||||
* 二项堆的每个结点都是二叉树的根结点。合并时将相同度的二叉树合并。
|
|
@ -0,0 +1,8 @@
|
|||
## CGA显示控制器(6845):(2个端口)
|
||||
|
||||
```
|
||||
显示控制索引端口:0x3d4
|
||||
存放光标位置的寄存器编号是14和15
|
||||
数据端口:0x3d5
|
||||
显存范围:0xb8000~0xbc000,每个字符对应2个字节,第1个字节是ASCII码,第2个字节是显示属性(低4位是前景色,高4位是背景色,KRGBIRGB,K闪烁I亮度。
|
||||
```
|
|
@ -0,0 +1,27 @@
|
|||
## 8042键盘控制器:(4个寄存器,2个端口,3个内部端口)
|
||||
|
||||
```
|
||||
寄存器:Status Register, Control Register, Input Buffer, Output Buffer
|
||||
读写Control Register:先向0x64写入命令(0x20读,0x60写),再通过0x60端口对Control Register进行相应的读或写的操作
|
||||
0键盘中断,1鼠标中断,2设置状态寄存器的bit2,3设置状态寄存器的bit4,4禁止键盘,5禁止鼠标,6翻译扫描码,7保留恒为0
|
||||
读Output Buffer:读0x60端口
|
||||
存储的是扫描码或命令响应
|
||||
写Input Buffer:写0x60端口
|
||||
存储的是命令及参数
|
||||
读Status Register:读0x64端口
|
||||
0输出缓冲器,1输入缓冲器,2系统标志,3输入缓冲器中是命令还是数据,4键盘是否被禁止,5发送超时,6接收超时,7奇偶校验
|
||||
内部端口:Input Port, Output Port, Test Port
|
||||
读写Output Port:先向0x64写入命令(0xd0读,0xd1写),再通过0x60端口对Output Port进行相应的读或写的操作
|
||||
读input port:先向0x64写入命令0xc0,再从0x60端口读出
|
||||
命令和状态端口:0x64
|
||||
共有12种参数:(0x20, 0x60, 0xa4, 0xa5, 0xa6, 0xaa, 0xad禁止键盘, 0xae打开键盘, 0xc0, 0xd0, 0xd1, 0xd2)
|
||||
0xa4:测试键盘密码是否被设置,结果放在0x60端口
|
||||
0xa5:设置键盘密码,结果通过0x60端口放在Input Buffer中
|
||||
0xa6:让通过0xa5命令设置的密码生效
|
||||
0xaa:自检,结果放在Output Buffer中,可通过0x60端口读取
|
||||
0xd2:写数据到Output Buffer,通过0x60端口写入,此功能用来模拟键盘发送的数据
|
||||
数据端口:0x60
|
||||
movb $0xdf,%al
|
||||
outb %al,0x60 打开A20地址线
|
||||
```
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
其实就是uart,只是Intel把它叫做**Programmable Communication Interface**而已。
|
||||
|
||||
串口控制器:(3个端口)
|
||||
|
||||
状态端口:0x3f8+5
|
||||
数据端口:0x3f8
|
|
@ -0,0 +1,24 @@
|
|||
参考自[CSDN博客_Mr.Phoebe](http://blog.csdn.net/u013007900/article/details/50408903)
|
||||
1. 8253芯片是可编程计数器/计时器(Counter/Timer)。因为计时和计数的本质是相同的,所以即可以叫计时器也可以叫计数器。
|
||||
2. 内部有三个完全相同的计数器,分别是计数器0,计数器1,计数器2.
|
||||
3. 端口号由引脚A1和A0确定。如果计数器0端口号为0x40,则计数器1为0x41,计数器2为0x42,控制端口为0x43。
|
||||
4. 8253编程的两条原则:
|
||||
* 必须先将控制字写入控制寄存器,即0x43端口。
|
||||
* 其次向计时器写入初值,初值必须符合控制字的规定。
|
||||
5. 控制字有8位,只能写入,不能读出。
|
||||
|
||||
| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|
||||
| --- | --- | --- | --- | -- | -- | -- | --- |
|
||||
| SC1 | SC0 | RW1 | RW0 | M2 | M1 | M0 | BCD |
|
||||
* D0:计数码选择。决定计数器在减1过程中采用的进位制,0表示二进制,1表示BCD计数制。
|
||||
* D3D2D1:工作方式选择。
|
||||
* 000:Interrupt on Terminal Count(计数结束产生中断)
|
||||
* 001:Hardware Retriggerable one-shot(可编程单稳态触发器)
|
||||
* 010:Rate Generator(分频器)
|
||||
* 011:Square Ware Mode(方波发生器)
|
||||
* 100:Software Triggered Strobe(软件触发选通脉冲)
|
||||
* 101:Hardware Triggered Strobe(硬件触发选通脉冲)
|
||||
* D5D4:读写格式控制。01,只读写低8位。10,只读写高8位。11,先读写低8位,后读写高8位。00,把当前计数值存入输出锁存器,供以后读取。
|
||||
* D7D6:计数器选择。00,选择计数器0。01,选择计数器1。10,选择计数器2。11,在8253中为非法编码,在8254中用于读回命令。
|
||||
|
||||

|
|
@ -0,0 +1,37 @@
|
|||
#### 8259A概述
|
||||
|
||||

|
||||
|
||||
8259A芯片是一个中断管理芯片,中断的来源除了来自于硬件自身的NMI中断和来自于软件的`INT n`指令造成的软件中断之外,还有来自于外部硬件设备的中断,这些中断是可屏蔽的。这些中断也都通过**PIC**(Programmable Interrupt Controller)进行控制,并传递给CPU。
|
||||
|
||||
一个8259A芯片的可以接最多8个中断源,但由于可以将2个或多个8259A芯片级连(cascade),并且最多可以级连到9个,所以最多可以接64个中断源。如今绝大多数的PC都拥有两个8259A,这样 最多可以接收15个中断源。
|
||||
|
||||
8259提供了两种屏蔽方式:1,简单方式,提供8位屏蔽字对应各个IR。2,特殊方式,允许CPU让低优先级的外设去中断高优先级的服务程序。
|
||||
|
||||
#### 内部结构
|
||||
|
||||

|
||||
|
||||
- IRR:中断请求寄存器8位,IR0~IR7每一位对应一个设备,共可接收8个设备的中断请求。
|
||||
- IMR:中断屏蔽寄存器为8位,设置需要屏蔽的中断请求
|
||||
- ISR:中断服务寄存器为8位,保存当前正在处理的中断请求
|
||||
- PR:优先权判别器。当多个中断同时发生时,将高优先级者优先传递给CPU。优先级选择方式有4种:1,完全嵌套方式,优先级从IR0到IR7依次降低。2,轮换方式A,一个中断完成后立即把它放到最低优先级的位置上。3,轮换方式B,CPU可以在任何时间规定最优优先级。4,查询方式,CPU访问中断状态寄存器。
|
||||
- 控制逻辑:向CPU发出中断请求信号INT,并接受CPU的中断响应信号INTA。
|
||||
- 数据总线缓冲器:保存数据总线的数据,传输命令控制字、状态字和中断类型码。
|
||||
- 读/写逻辑:确定数据总线缓冲器中数据的传输方向,选择内部的各命令字寄存器。RD为读,WR为写,AO为I/O端口识别,CS为设备选择。
|
||||
- 级联缓冲/比较器:主从控制器的级联是由级联总线CAS0,CAS1,CAS2实现的。
|
||||
|
||||
#### 工作原理
|
||||
|
||||

|
||||
|
||||
#### 实例:两个中断控制器连接
|
||||
|
||||

|
||||
|
||||
Master 8259A:
|
||||
0x20: ICW1,OCW2,OCW3,IRR,ISR
|
||||
0x21: ICW2,ICW3,ICW4,IMR, address registers
|
||||
Slave 8259A:
|
||||
0xA0: ICW1,OCW2,OCW3,IRR,ISR
|
||||
0xA1: ICW2,ICW3,ICW4,IMR, address registers
|
|
@ -0,0 +1,8 @@
|
|||
## fast gate A20: (1个端口0x92)
|
||||
|
||||
```
|
||||
inb $0x92,%al
|
||||
orb $0x2,%al
|
||||
outb %al,$0x92
|
||||
```
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#### 简介
|
||||
|
||||
*Advanced Configuration and Power Interface*,操作系统用它来查找和配置计算机硬件,让未使用的硬件休眠来进行电源管理,并进行状态监控。
|
||||
|
||||
##### RSDP
|
||||
|
||||
RSDP(Root System Description Pointer)是ACPI编程接口用到的数据结构。如下所示:
|
||||
|
||||
```c
|
||||
struct acpi_table_rsdp {
|
||||
uint8_t signature[8]; /* "RSD PTR " */
|
||||
uint8_t checksum; /* first 20 bytes must add up to 0 */
|
||||
uint8_t oem_id[6];
|
||||
uint8_t revision; // ACPI的修订号,大的修定号向后兼容低的修订号。
|
||||
uint32_t rsdt_physical_address; /* 32-bit physical address of RSDT */
|
||||
uint32_t length; /* table length */
|
||||
uint64_t xsdt_physical_address; /* 64-bit physical address of XSDT */
|
||||
uint8_t extended_checksum; /* the entire table must add up to 0 */
|
||||
uint8_t reserved[3];
|
||||
} __packed;
|
||||
```
|
||||
|
||||
RSDP有两个可能的位置:一是在EBDA(Extended BIOS Data Area)的头1KB,EBDA是16位的实模式段,位于0x40e;一是在内存区域从0x000e0000到0x000fffff。
|
||||
|
||||
RSDT(Root System Description Table)是ACPI编程接口用到的数据结构。
|
||||
|
||||
XSDT(eXtended System Descriptor Table)是RSDT的64位版本。
|
|
@ -0,0 +1,56 @@
|
|||
#### 简介
|
||||
|
||||
(Advanced Programmable Interrupt Controller)是一个与8259A兼容的高级中断处理器。APIC分为两层:Local APIC和I/O APIC。
|
||||
|
||||

|
||||
|
||||
- I/O APIC I/O APIC是用来与外部设备通讯的,它安装在主板上,它完成了APIC最主要的功能:中断处理。 I/O APIC提供了两个模式,普通模式和8259A兼容模式。xv6在单核环境下,会选择使用8259A兼容模式,而在多处理器环境下,则会使用普通模式。
|
||||
- Local APIC是APIC的顶层,每个核都有一个对应的Local APIC,LAPIC内置在CPU内部。它负责进行多处理器之间的中断传输,屏蔽中断,还提供了一个可编程的Timer。由于I/O APIC已经提供了中断处理的功能,Local APIC只是起辅助作用,可以屏蔽不用。
|
||||
|
||||
#### APIC的中断映射
|
||||
|
||||
| IRQ | Using SERIRQ | Direct form Pin | Using PCI Message | Internal Modules |
|
||||
| ------ | ------------ | --------------- | ----------------- | ----------------------------- |
|
||||
| 0 | | | | |
|
||||
| 1 | | | | |
|
||||
| 2 | | | | |
|
||||
| 3 | | | | |
|
||||
| 4 | | | | |
|
||||
| 5 | | | | |
|
||||
| 6 | | | | |
|
||||
| 7 | | | | |
|
||||
| 8 | | | | |
|
||||
| 9 | | | | |
|
||||
| 10 | | | | |
|
||||
| 11 | | | | |
|
||||
| 12 | | | | |
|
||||
| 13 | | | | |
|
||||
| 14 | | | | |
|
||||
| 15 | | | | |
|
||||
| 16~19 | PIRQA~PIRQD | PIRQA~PIRQD | Yes | Internal devices are routable |
|
||||
| 20~23 | N/A | PIRQH | Yes | |
|
||||
|
||||
#### 控制方式
|
||||
|
||||
APIC通过修改该单元的寄存器来实现控制,但APIC的相关寄存器被映射到了物理地址。
|
||||
|
||||
- Local APIC默认映射到物理地址`0xFEE0 0000`
|
||||
- IO APIC 默认映射到物理地址`0xFEC0 0000`
|
||||
|
||||
#### PRT表的格式
|
||||
|
||||
Programmable Redirection Table(24*64bit),IOAPIC上有24个interrupt pin,每一个pin都对应一个RTE,所以针对每一个interrupt pin都可以单独设定它的mask,触发方式(level,edge trigger),中断管脚的极性,传送方式,传送状态,目的地,中断向量等。
|
||||
|
||||
| bit | 意义 |
|
||||
| ------ | ------------------------------------------------------------ |
|
||||
| 63:56 | Destination Field(Physical Mode:APIC ID, Logical Mode:一组CPU) |
|
||||
| 55:17 | Reserved |
|
||||
| 16 | Interrupt Mask(1:屏蔽中断,0:中断发给LAPIC) |
|
||||
| 15 | Trigger Mode(1:Level,电平触发。0:Edge,边沿触发) |
|
||||
| 14 | Remote IRR,只读,只对Level触发有效。(1:LAPIC接收了该中断,0:LAPIC写EOI) |
|
||||
| 13 | Interrupt Input Pin Polarity(INTPOL)(1:低电平,0:高电平) |
|
||||
| 12 | Delivery Status(0:IDEL,当前没有中断,1:Send Pending,IOAPIC已收到中断,但由于某种原因还没有发给LAPIC) |
|
||||
| 11 | Destination Mode(0:Physical Mode, 1: Logical Mode) |
|
||||
| 10:8 | Delivery Mode(0:Fixed, 1:Lowest Priority, 2: SMI, 4: NMI, 5:INIT, 7:ExtINT ) |
|
||||
| 7:0 | Interrupt Vector(0x10~0xFE, X86架构的前16个vector被系统预留) |
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
#### 参考资料
|
||||
|
||||
[FU540-C000-v1.0](https://static.dev.sifive.com/FU540-C000-v1.0.pdf)
|
||||
|
||||
[Getting-Started-Guide](https://static.dev.sifive.com/HiFive-Unleashed-Getting-Started-Guide-v1p1.pdf)
|
||||
|
||||
#### 简介
|
||||
|
||||
HiFive Unleashed是U540 Soc的Linux开发平台,是一个多核RISC-V SoC。硬件配置如下:
|
||||
|
||||
| 硬件 | 属性 | 手册对应章节 |
|
||||
| ------------ | ------------------------------------------ | ------------ |
|
||||
| 内存 | 8G DDR4 | 5, 12, 20 |
|
||||
| 以太网端口 | 千兆 | 19 |
|
||||
| JTAG接口 | 用于调试 | 23, 34 |
|
||||
| ChipLink | TileLink协议的片外串行化 | 22 |
|
||||
| SD卡槽 | | |
|
||||
| PCIe | FMC + 低速I/O扩展 | |
|
||||
| E51 Core 0 | RV64IMAC,(M,U mode),监控核 | 3 |
|
||||
| U54 Core 1-4 | RV64GC,(M,S,U mode),应用核 | 4 |
|
||||
| L2 cache | 2M | 11 |
|
||||
| 中断 | 平台级中断控制器PLIC,计时器中断,软件中断 | 8, 9, 10 |
|
||||
| 串口 | uart0, uart1 | 13 |
|
||||
| QSPI 0-3 | 串行外部接口 | 16 |
|
||||
| PWM 0-1 | 脉冲宽度调制器 | 14 |
|
||||
| I2C 0 | 集成电路控制器 | 15 |
|
||||
| GPIO | 通用I/O引脚 | 17 |
|
||||
| OTP | 一次可编程存储器 | 18 |
|
||||
|
||||
需要:开发板,USB线,电源适配器,可选配网线或FMC扩展卡
|
||||
|
||||
#### 时钟和复位(手册第7章)
|
||||
|
||||
时钟和复位是通过电源复位时钟中断(PRCI)架构来管理的。
|
||||
|
||||
#### 启动过程(手册第6章)
|
||||
|
||||
FU540-C000支持从多种源来启动,这是通过模式选择(MSEL[3:0])引脚来实现的。而3个QSPI接口则控制从哪里下载media(SPI flash或SD卡)。
|
||||
|
||||
| MSEL | FSBL的位置 | BBL的位置 | 作用 |
|
||||
| ---- | -------------- | -------------- | --------------------------------- |
|
||||
| 0000 | - | - | 无限循环等待调试 |
|
||||
| 0001 | - | - | 跳至0x2000_0000(内存映射OSPI0) |
|
||||
| 0010 | - | - | 跳至0x3000_0000(内存映射OSPI1) |
|
||||
| 0011 | - | - | 跳至0x4000_0000(未缓存的ChipLink) |
|
||||
| 0100 | - | - | 跳至0x6000_0000(缓存的ChipLink) |
|
||||
| 0101 | QSPI0 x1 flash | QSPI0 x1 flash | - |
|
||||
| 0110 | QSPI0 x4 flash | QSPI0 x4 flash | 从flash 获取镜像 |
|
||||
| 0111 | QSPI1 x4 flash | QSPI1 x4 flash | - |
|
||||
| 1000 | QSPI1 x1 SD | QSPI1 x1 SD | - |
|
||||
| 1001 | QSPI2 x1 flash | QSPI2 x1 flash | - |
|
||||
| 1010 | QSPI0 x4 flash | QSPI1 x1 SD | - |
|
||||
| 1011 | QSPI2 x1 SD | QSPI2 x1 SD | 从SD卡获取镜像 |
|
||||
| 1100 | QSPI1 x1 flash | QSPI2 x1 SD | - |
|
||||
| 1101 | QSPI1 x4 flash | QSPI2 x1 SD | - |
|
||||
| 1110 | QSPI0 x1 flash | QSPI2 x1 SD | - |
|
||||
| 1111 | QSPI0 x4 flash | QSPI2 x1 SD | 默认启动模式 |
|
||||
|
||||
##### 1. 复位向量
|
||||
|
||||
一加电,所有的核都跳到ROM 0x1004处执行,ROM中的内容如下:
|
||||
|
||||
```
|
||||
0x1000 MSEL的值
|
||||
0x1004 auipc t0, 0 # t0保存了当前指令的地址,即0x1004
|
||||
0x1008 lw t1, -4(t0) # t1保存的内存0x1000的值,即MSEL的值
|
||||
0x100c slli t1, t1, 0x3 # t1 = t1 << 3,即8*t1
|
||||
0x1010 add t0, t0, t1 # t0 += t1
|
||||
0x1014 lw t0, 252(t0) # t0保存的内存地址0x1000+0x100+8*MSEL处的值
|
||||
0x1018 jr t0 # 实际上最终跳转的位置由MSEL的值决定
|
||||
```
|
||||
|
||||
当MSEL>4的时候,均是跳转到ROM 0x1_0000处执行,即ZSBL。
|
||||
|
||||
##### 2. ZSBL
|
||||
|
||||
ZSBL是零阶段的引导程序,它负责从GPT下载FSBL。编号为0的核首先配置时钟分频器,然后寻找GUID为`5B193300-FC78-40CD-8002-E86C45580B47`的分区。它首先下载GPT头(位置在512字节~604字节),然后依次扫描分区表直到找到分区,接下来将该分区的内容(即FSBL)下载到内存0x0800_0000处,最后跳转到该地址执行。
|
||||
|
||||
ZSBL是通过MSEL的值来决定FSBL所在的分区的。
|
||||
|
||||
##### 3. FSBL
|
||||
|
||||
FSBL是第一阶段的引导,它负责将内核载入内存。
|
||||
|
||||
- 通过配置和运行片上PLL将频率提升至1GHz。
|
||||
- 配置DDR PLL,以太网PHY和控制器。
|
||||
- 设置GEM,GXL,TX,PLL为125MHz并重置之。
|
||||
- 如果有外部PHY,重置之。
|
||||
- 从GUID号为`2E54B353-1271-4842-806F-E436D6AF69851`的分区下载BBL。
|
||||
- Scan OTP for the chip serial number
|
||||
- 将DTB复制到内存
|
||||
- 跳转到`0x8000_0000`
|
||||
|
||||
FSBL通过MSEL的值来寻找BBL所在的分区。
|
||||
|
||||
##### 4. BBL
|
||||
|
||||
BBL在`0x8000_0000`,它提供SBI并模拟RISC-V指令。Linux在`0xffff_ffe0_0000_0000`处。
|
||||
|
||||
##### 5. 启动方法
|
||||
|
||||
- Flash Bit-Banged x1
|
||||
- Flash 内存映射x1
|
||||
- Flash 内存映射x4
|
||||
- SD卡Bit-Banged x1
|
||||
|
||||
#### 软件开发流程
|
||||
|
||||
1. 安装需要的包
|
||||
|
||||
```shell
|
||||
sudo apt-get install autoconf automake autotools-dev bc bison \
|
||||
build-essential curl flex gawk gdisk git gperf libgmp-dev \
|
||||
libmpc-dev libmpfr-dev libncurses-dev libssl-dev libtool \
|
||||
patchutils python screen texinfo unzip zlib1g-dev
|
||||
```
|
||||
|
||||
2. 安装free-u-sdk
|
||||
|
||||
```shell
|
||||
git clone https://github.com/sifive/freedom-u-sdk.git
|
||||
cd freedom-u-sdk
|
||||
git submodule update --init --recursive
|
||||
unset RISCV
|
||||
make
|
||||
```
|
||||
编译时问题解决:
|
||||
|
||||
- `Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/\${ <-- HERE (\[^ \t=:+{}]+)}/ at /data/szx/Code/freedom-u-sdk/work/buildroot_initramfs/host/usr/bin/automake line 3936.`
|
||||
|
||||
以上问题的解决方法:给大括号转义,变成如下形式`\{`
|
||||
|
||||
- `./../misc/create_inode.c:395:18: error: conflicting types for ‘copy_file_range’`
|
||||
|
||||
以上问题的解决方法:这是由于自定义函数和库函数重名了,解决方法就是给自定义函数换个名字。首先,通过grep命令找到所有定义和使用了copy_file_range函数的地方,然后把所有的copy_file_range函数更名为copy_file_chunk。2个文件共计8处需要修改。
|
||||
|
||||
> 编译完毕,显示如下信息:
|
||||
>
|
||||
> This image has been generated for an ISA of rv64imafdc and an ABI of lp64d
|
||||
> Find the image in work/bbl.bin, which should be written to a boot partition
|
||||
>
|
||||
> To completely erase, reformat, and program a disk sdX, run:
|
||||
> sudo make DISK=/dev/sdX format-boot-loader
|
||||
> ... you will need gdisk and e2fsprogs installed
|
||||
|
||||
3. 把镜像复制到SD卡
|
||||
|
||||
```
|
||||
sudo umount /dev/exb # 假设SD卡名叫exb,先要从文件系统中卸载掉
|
||||
sudo gdisk /dev/exb # 为exb创建GPT
|
||||
# p打印分区信息,d删除分区,o清除所有分区数据,w写数据(即将更改作用于设备)
|
||||
sudo make DISK=/dev/exb format-boot-loader # 将系统写入SD卡
|
||||
```
|
||||
写卡时的问题与修复:
|
||||
|
||||
- 写低速SD卡有可能会失败,多重复几次即可成功。
|
||||
|
||||
4. 在开发板上运行镜像
|
||||
|
||||
1. 拔电源,关开关。(关开关并不能代替拔电源。)
|
||||
2. 确保风扇已连接。
|
||||
3. 将DIP开关拔到左边(左边为1,右边为0),使MSEL处于1111模式。这是默认的启动模式。
|
||||
4. 插入安装了BBL+Linux的SD卡。
|
||||
5. 连接网线和USB线。
|
||||
6. 插上电源,风扇开始转动。
|
||||
7. 打开开关,30秒后LED灯开始闪烁。
|
||||
|
||||
有两种方式连到开发板,ssh和串口。串口慢且不能接收文件,应尽量选择ssh方式。
|
||||
|
||||
- ssh连接
|
||||
|
||||
电源开启后,默认的freedom-u-sdk镜像会通过DHCP获取IP地址,并开启ssh服务。
|
||||
|
||||
`ssh root@ip地址`登陆,密码:sifive
|
||||
|
||||
- USB连接
|
||||
|
||||
`sudo screen /dev/ttyUSB1 115200`
|
||||
|
||||
用户名:root,密码:sifive
|
||||
|
||||
5. 在qemu中运行镜像
|
||||
|
||||
`make qemu`
|
||||
|
||||
编译时问题的解决:
|
||||
|
||||
- `Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/\${ <-- HERE ([^ \t=:+{}]+)}/ at /data/szx/Code/freedom-u-sdk/work/buildroot_rootfs/host/usr/bin/automake line 3936.`
|
||||
|
||||
以上问题的解决方法依然是给大括号转义。
|
||||
|
||||
- `./../misc/create_inode.c:395:18: error: conflicting types for ‘copy_file_range’`
|
||||
|
||||
以上问题的解决方法依然是给本地函数重命名。
|
||||
|
||||
#### 连接器引脚分配
|
||||
|
||||
##### FMC
|
||||
|
||||
FPGA Mezzanine Card
|
||||
|
||||
##### 低速I/O扩展
|
||||
|
||||
##### MicroUSB
|
||||
|
||||
##### SD卡
|
||||
|
||||
##### JTAG
|
|
@ -0,0 +1,18 @@
|
|||
#### 两种I/O
|
||||
|
||||
1. CPU只能读取三个地方的数据:寄存器,内存,端口
|
||||
1. CPU与外设有两种交互方式:内存映射IO(MMIO)与端口IO(PMIO)
|
||||
|
||||
##### 端口映射I/O
|
||||
|
||||
1. 端口读写指令只有2条:in, out 。这两个指令的硬件实现本质上和读写内存是相同的。
|
||||
1. 端口地址范围:0~65535 (本机端口:/proc/ioports)
|
||||
1. 只能用ax或al来存放与端口交换的数据
|
||||
|
||||
##### 内存映射I/O
|
||||
|
||||
很多计算机体系结构都没有单独的设备访问指令,取而代之的是让设备拥有固定的内存地址,然后通过内存读写实现设备读写。实际上现代x86体系结构就在大多数高速设备上(如网络、磁盘、显卡控制器)使用了该技术,叫做内存映射I/O。
|
||||
|
||||
#### DMA
|
||||
|
||||
内存映射(MMIO和PMIO)作为一种CPU对I/O设备(CPU-to-device)的通信方法,并不影响DMA(直接内存访问), 因为DMA是一种绕过CPU的内存对设备(memory-to-device)的通信方法。
|
|
@ -0,0 +1,15 @@
|
|||
#### NUMA简介
|
||||
|
||||
numa把一台计算机分成多个节点(node),每个节点内部拥有多个CPU,节点内部使用共有的内存控制器,节点之间是通过互联模块进行连接和信息交互。
|
||||
|
||||
#### NUMA和SMP的区别(CK老师的解释)
|
||||
|
||||
NUMA和SMP的区别在性能上。SMP是对称多处理,NUMA是非统一内存访问。
|
||||
|
||||
SMP的每一颗处理器到内存的距离都是一样的。传统上所有处理器都连接着北桥芯片(即内存控制器),这样的结构是对称的,可以认为是SMP。
|
||||
|
||||
现在,内存控制器做在处理器里面了,这样每一个处理器都可以控制一部分内存。要访问另一颗处理器控制的内存,就必须通过处理器的通信线路,这样访问自己的内存就快,访问别人的内存就慢,这样就存在一个处理器亲和力的问题。解决方法就是内核线程要绑定到一个处理器的一个核心中,不要老跑来跑去。
|
||||
|
||||
有这样一个结论:任何针对单核处理器所做的优化,在多核上面都要创建一个新的Memory Model。
|
||||
|
||||
对内存有兴趣的同学可以看两个文档:**All Programmer should Know About Memory**和**Tutorial On Memory Model**。前提是要先清楚**Sequential Consistency**的概念。
|
|
@ -0,0 +1 @@
|
|||
- TLB(Translation Lookaside Buffer):翻译后备缓冲器,是在MMU中的一个对PTE的小的缓存。由于MMU比L1缓存快的多,所以可以提高CPU执行的效率。但如果PTE更新了,则需要刷新TLB。
|
|
@ -0,0 +1,26 @@
|
|||
#### 简介
|
||||
|
||||
Intel的虚拟化技术包含了三方面的虚拟化:**CPU虚拟化**是VT-x或VT-i,**内存虚拟化**是EPT(Extended Page Table),**I/O虚拟化**是VT-d(Intel Virtualization Technology for Directed I/O)。
|
||||
|
||||
VT-x是Intel虚拟化技术的指令集,在x86平台上的VT技术被称为VT-x(在itanium平台上的VT技术被称为VT-i)。
|
||||
|
||||

|
||||
|
||||
它引入了VMX操作模式,有两种VMX操作模式:**VMX root operation**是为VMM/Hypervisor使用的,其行为与传统的IA32一样;**VMX not-root operation**则是为Guest使用的,是在VMM控制下的IA32环境。Guest是运行在Ring0下的,Guest运行的指令一般可以直接控制硬件,只有需要执行特殊指令才会切换到VMM。
|
||||
|
||||
VMX操作流程:
|
||||
|
||||
1. 执行`vmxon`指令进入VMX操作模式。
|
||||
2. 执行`vmlaunch`或`vmresume`指令产生VMEntry操作,进入到Guest OS。
|
||||
3. Guest OS执行特权指令导致VMExit操作,陷入到VMM。
|
||||
4. VMM可以通过`vmxoff`指令退出VMX操作。
|
||||
|
||||

|
||||
|
||||
VT-x设计了一个Virtual-Machine Control Structure(VMCS)的数据结构,CPU在发生VMEntry或VMExit时都会查询和更新VMCS。
|
||||
|
||||
- 0~3字节,VMCS版本标识
|
||||
- 4~7字节,VMX中止的原因
|
||||
- 8字节~end,数据区。由3个部分组成:控制区域(虚拟执行控制域,VMExit控制域,VMEntry控制域,VMExit信息域),Guest区(寄存器状态,非寄存器状态),Host区(主要是VMM运行时的状态)。
|
||||
|
||||
[Intel Virtualization Technology for Directed I/O: Architecture Specification](https://software.intel.com/sites/default/files/managed/c5/15/vt-directed-io-spec.pdf)
|
After Width: | Height: | Size: 200 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 280 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 98 KiB |
|
@ -0,0 +1,11 @@
|
|||
* 总线是电路的通路,它连接了CPU芯片,显卡芯片,内存芯片和I/O控制芯片等。
|
||||
|
||||
* 总线标准有XT,AT(ISA),MCA,EISA,PCI,USB,用以兼容不同厂家的设备。
|
||||
|
||||
* ISA总线结构
|
||||
|
||||

|
||||
|
||||
* PCI总线
|
||||
|
||||

|
|
@ -0,0 +1,10 @@
|
|||
#### 概览
|
||||
|
||||
下图是对Rocket_chip的概览。
|
||||
|
||||

|
||||
|
||||
- Rocket Tile:一个Rocket core和对应的L1缓存。
|
||||
- htif:Host/Target interface,Host指ARM PS,Target指RISC-V。
|
||||
- coherence manager:一致性管理器,管理L1缓存和L2缓存的一致性。
|
||||
- TileLink:Rocet Tile与一致性管理器之间的通信协议。
|
|
@ -0,0 +1,24 @@
|
|||
- CPU(中央处理单元,或处理器)其实只是在执行一个非常简单的循环:从一个被称为『程序计数器』的寄存器中获取一个内存地址,从个地址读出机器指令,增加程序计数器的值,执行机器指令,不断反复。
|
||||
|
||||
|
||||
- 速度最快的数据存储器是处理器的寄存器组。
|
||||
|
||||
| X86寄存器类型 | 数量 | 寄存器名称 |
|
||||
| ------------- | ------- | ------------------------------------------- |
|
||||
| 通用寄存器 | 8(32位) | %eax,%ebx,%ecx,%edx,%edi,%esi,%ebp, %esp |
|
||||
| 程序计数器 | 1(32位) | %eip(instruction pointer) |
|
||||
| 浮点寄存器 | 8(80位) | 名称未知 |
|
||||
| 控制寄存器 | 4 | %cr0,%cr2,%cr3,%cr4 |
|
||||
| 调试寄存器 | 4 | %dr0,%dr1,%dr2,%dr3 |
|
||||
| 段寄存器 | 6 | %cs,%ds,%es,%fs, %gs,%ss |
|
||||
| 伪寄存器 | 2 | %gdtr,%ldtr |
|
||||
|
||||
- 缓存是主存和寄存器在速度和大小上的折衷。
|
||||
|
||||
- 80386CPU
|
||||
|
||||

|
||||
|
||||
- 80386引脚
|
||||
|
||||

|
|
@ -0,0 +1,11 @@
|
|||
### 硬盘控制器:(ide控制器最多支持4个硬盘)
|
||||
0x1f0:读数据,注:应该是4字节大的端口
|
||||
0x1f2:需要读写的扇区数量
|
||||
0x1f3:LBA参数的0~7位 //LBA参数即要读写扇区的起始扇区号
|
||||
0x1f4:LBA参数的8~15位
|
||||
0x1f5:LBA参数的16~23位
|
||||
0x1f6:0~3(LBA24~27位),4(0主盘1从盘),6(1LBA模式,0CHS模式),5和7(固定为1)
|
||||
0x1f7:状态和命令寄存器
|
||||
0x20命令读取硬盘
|
||||
第7位为1表示硬盘忙
|
||||
第7位为0,第3位为1表示硬盘空闲,可以读取指令
|
|
@ -0,0 +1,9 @@
|
|||
## 并口控制器:(3个端口)
|
||||
|
||||
```
|
||||
命令端口:0x37a
|
||||
状态端口:0x379
|
||||
数据端口:0x378
|
||||
```
|
||||
|
||||
##
|
|
@ -0,0 +1,99 @@
|
|||
### 功能测试
|
||||
|
||||
在ZedBoard自带的SD卡上就有一个简单的Linux镜像,用于展示Zedboard的基本功能。
|
||||
|
||||
#### 1. 连接跳线
|
||||
|
||||
确保跳线按如下形式连接:
|
||||
|
||||
| 标识符 | 连接形式 | 意义 |
|
||||
| ------ | -------- | -------- |
|
||||
| JP11 | GND | 使用PLL |
|
||||
| JP10 | 3V3 | SD卡启动 |
|
||||
| JP9 | 3V3 | SD卡启动 |
|
||||
| JP8 | GND | SD卡启动 |
|
||||
| JP7 | GND | JTAG级联 |
|
||||
| JP6 | Shorted | SD卡启动 |
|
||||
| J18 | 1.8V | 文档要求 |
|
||||
|
||||
#### 2. 启动系统
|
||||
|
||||
插入SD卡,开发板上Uart接口与电脑相连,连接电源,拨动开关,命令窗口中输入`sudo minicom -D /dev/ttyACM0`。看到提示符`zed-boot>`即表示成功进入U-BOOT。
|
||||
|
||||
输入`printenv`可以看到环境变量。
|
||||
|
||||
#### 3. 启动Linux
|
||||
|
||||
输入`run sdboot`开开始启动Linux,当看到提示符`zynq>`即表示成功进入linux。可以看到开发板上的显示屏显示出了图案。
|
||||
|
||||
```
|
||||
read_sw # 读取开关的状态
|
||||
write_led 0xF0 # 使左边4个LED灯开,右边4个关
|
||||
write_led 0xF # 使左边4个关,右边4个关
|
||||
write_led 0 # 使8个LED灯关闭
|
||||
poweroff # 关闭系统
|
||||
```
|
||||
|
||||
### 运行Rocket
|
||||
|
||||
#### 1. 制作SD卡
|
||||
|
||||
```
|
||||
git clone https://github.com/ucb-bar/fpga-zynq.git
|
||||
cd fpga-zynq/zedboard
|
||||
make fetch-images
|
||||
# 电脑上插入SD卡,假定挂载在/mnt/abc
|
||||
make load-sd SD=/mnt/abc
|
||||
# SD卡再插入开发板
|
||||
```
|
||||
|
||||
> boot.bin:system.bit + u-boot.elf + zynq_fsbl.elf
|
||||
>
|
||||
> uImage:ARM Linux
|
||||
>
|
||||
> uramdisk.image.gz:包含根文件系统的RAMDisk
|
||||
>
|
||||
> devicetree.dtb:设备树
|
||||
>
|
||||
> riscv/vmlinux:RISC-V Linux内核
|
||||
>
|
||||
> riscv/root.bin:包含根文件系统的RAMDisk(for RISC-V Linux)
|
||||
|
||||
#### 2. 启动Linux
|
||||
|
||||
在U-BOOT界面输入`run sdboot`启动Linux,用户名和密码均为root。
|
||||
|
||||
然而这有个问题,就是无法启动riscv核。我推测是riscv核根本就没有烧写到开发板里。
|
||||
|
||||
### 硬件结构
|
||||
|
||||
#### 跳线
|
||||
|
||||
| 标识符 | 描述 | 默认设置 | 功能 |
|
||||
| -------- | --------------------------- | --------------------- | --------------------------------------- |
|
||||
| JP1 | 卖克风输入bias | Open-无Electret卖克风 | 短接使能偏压 |
|
||||
| JP2 | Vbus 5V 使能 | Open-5V无连接 | 短接使能5V输出到OTG连接器 |
|
||||
| JP3 | Vbus电容器设置 | Open-设备模式 | 短接用于Host模式,Open用于设备或OTG模式 |
|
||||
| JP4 | CFGBVS选择 | 无填充 | |
|
||||
| JP5 | PUDC选择 | 无填充 | |
|
||||
| JP6 | MIO[0] | Short | SD卡启动 |
|
||||
| JP7 | Boot_Mode[3]/MIO[2] | GND-级联JTAG | JTAG模式。 |
|
||||
| JP8,9,10 | Boot_Mode[0,1,2]/MIO[3,4,5] | 110-SD卡 | 选择启动设备 |
|
||||
| JP11 | Boot_Mode[4]/MIO[6] | GND-PLL用 | PLL选择。GND使用PS PLL,VCC绕过PS PLL. |
|
||||
| JP12 | XADC | Open | |
|
||||
| JP13 | JTAG PS-RST | Open | 短接将JTAG PROG-RST连接到PS Reset. |
|
||||
| JP18 | Vadj选择 | 1.8V | |
|
||||
|
||||
#### 启动模式
|
||||
|
||||
| | MIO6/JP11 | MIO5/JP10 | MIO4/JP9 | MIO3/JP8 | MIO2/JP7 |
|
||||
| -------------- | --------- | --------- | -------- | -------- | -------- |
|
||||
| JTAG模式(级联) | | | | | 0 |
|
||||
| JTAG模式(独立) | | | | | 1 |
|
||||
| 启动设备(JTAG) | | 0 | 0 | 0 | |
|
||||
| 启动设备(QSPI) | | 1 | 0 | 0 | |
|
||||
| 启动设备(SD卡) | | 1 | 1 | 0 | |
|
||||
| PLL(使用) | 0 | | | | |
|
||||
| PLL(绕过) | 1 | | | | |
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
LDR
|
|
@ -0,0 +1,846 @@
|
|||
RISC-V ISA手册
|
||||
|
||||
本手册分为两卷,上卷讲用户级指令,下卷讲特权级指令。
|
||||
|
||||
### 一些待解决的问题
|
||||
|
||||
#### 1. riscv里的ecall指令与x86里的int指令的异同。
|
||||
|
||||
ecall是用来触发异常的,而int是用来触发中断的,感觉它们用的地方也很相似,那么它们之间有什么异同呢?
|
||||
|
||||
#### 2. 为什么即有LW指令也有LWU指令,而只有SW指令没有SWU指令呢?
|
||||
|
||||
如果无符号数的宽度小于寄存器的宽度,执行普通的LW指令就会有问题,因为它会进行符号扩展,这就需要LWU指令把高位全都填充为0。而把数保存到寄存器中就不存在这样的问题,因为数的大小和占用的内存大小是完全一致的。
|
||||
|
||||
#### 3. 为什么有算术右移而没有算术左移呢?
|
||||
|
||||
|
||||
|
||||
### RISC-V 手册上卷(riscv-spec-v2.2)
|
||||
|
||||
#### 1. 介绍
|
||||
|
||||
设计目标。不使用商业ISA的原因。不使用OpenRISC的原因。
|
||||
|
||||
1. RISC-V ISA概述
|
||||
|
||||
共有3种类型的指令:基本指令,标准扩展指令和特殊扩展指令。基本指令被命名为I,是不可更改,必须被包含的指令,它们都是整数指令。标准扩展指令包括M(扩展乘除指令),A(扩展原子指令),F(单精度浮点指令扩展),D(双精度浮点指令扩展),G(IMAFD五种指令的总和,提供了完整的通用标量指令集,是默认工具链支持的指令集)。特殊扩展指令只用于特定领域,不适用于全部通用领域。
|
||||
|
||||
2. 指令长度编码
|
||||
|
||||
基本指令固定是32位的,然而扩展指令可以是变长的,但必须是16的倍数。当低2位不是11时指令长度为16位,当低2位是11时指令长度为32位,当低5位为11111时指令长48位,当低6位为111111时指令长64位,当低7位全1则要计算[14:12]位的数才能得出指令长度。
|
||||
|
||||
基本指令集是小端系统,但扩展指令集即可做成小端也可做成大端。
|
||||
|
||||
3. 异常、陷阱和中断
|
||||
|
||||
异常是指令错误引起的,陷阱是线程自主调用的,中断是外部事件引起的。
|
||||
|
||||
#### 2. RV32I指令集
|
||||
|
||||
RV32I是基本指令集,它被设计足以支持一个现代的操作系统,而且除了A扩展外它基本上能模拟所有的其它扩展。RV32I共计47条指令。
|
||||
|
||||
##### 1. 程序员模型
|
||||
|
||||
用户可见的寄存器有3种:1个恒为0的x0,31个通用寄存器x1-x31,pc。
|
||||
|
||||
##### 2. 基本指令格式
|
||||
|
||||
在基本ISA里,所有指令都是32位且必须4字节对齐。共有四种基本格式:R-type是寄存器-寄存器操作,I-type是寄存器-立即数操作,S-type,U-type。
|
||||
|
||||
立即数字段里放的是立即数的位置,而不是它本身。
|
||||
|
||||
R-type: **funct7**[31:25] **rs2**[24:20] **rs1**[19:15] **funct3**[14:12] **rd**[11:7] **opcode**[6:0]
|
||||
|
||||
I-type: **imm[11:0]**[31:20] **rs1**[19:15] **funct3**[14:12] **rd**[11:7] **opcode**[6:0]
|
||||
|
||||
S-type: **imm[11:5]**[31:25] **rs2**[24:20] **rs1**[19:15] **funct3**[14:12] **imm[4:0]**[11:7] **opcode**[6:0]
|
||||
|
||||
U-type: **imm[31:12]**[31:12] **rd**[11:7] **opcode**[6:0]
|
||||
|
||||
##### 3. 立即数编码的变体
|
||||
|
||||
B是S的变体,J是U的变体
|
||||
|
||||
B-type: **imm[12]imm[10:5]**[31:25] **rs2**[24:20] **rs1**[19:15] **funct3**[14:12] **imm[4:1]imm[11]**[11:7] **opcode**[6:0]
|
||||
|
||||
J-type:**imm[20]imm[10:1]imm[11]imm[19:12]**[31:12] **rd**[11:7] **opcode**[6:0]
|
||||
|
||||
##### 4. 整数计算指令(共计22条指令)
|
||||
|
||||
| imm+rs1+funct3+rd+opcode | funct3 | opcode | 意义 |
|
||||
| ---------------------------------------- | ------ | ------ | ---------------------------------------- |
|
||||
| ADDI rd, rs1, imm | ADDI | OP-IMM | rd = rs1 + imm |
|
||||
| SLTI rd, rs1, imm (set less than immediate) | SLTI | OP-IMM | rd = (rs1<imm) ? 1 : 0(用于有符号数) |
|
||||
| SLTIU rd, rs1, imm | SLTIU | OP-IMM | rd = (rs1<imm) ? 1 : 0(用于无符号数);**注sltiu rd,rs,1的意思是rd = (rs==0)?1:0;** |
|
||||
| ANDI rd, rs1, imm | ANDI | OP-IMM | rd = rs1 & imm |
|
||||
| ORI rd, rs1, imm | ORI | OP-IMM | rd = rs1 \| imm |
|
||||
| XORI rd, rs1, imm | XORI | OP-IMM | rd = rs1 ^ imm |
|
||||
| SLLI rd, rs1, imm | SLLI | OP-IMM | rd = rs1 << imm[4:0] |
|
||||
| SRLI rd, rs1, imm | SRLI | OP-IMM | rd = (unsigned) rs1 >> imm[4:0] |
|
||||
| SRAI rd, rs1, imm | SRAI | OP-IMM | rd = (signed) rs1 >> imm[4:0] |
|
||||
|
||||
| imm+rd+opcode | opcode | 意义 |
|
||||
| ---------------------------------------- | ------ | --------------------- |
|
||||
| LUI rd, imm (load upper immediate) | LUI | rd = (imm << 12) |
|
||||
| AUIPC rd, imm (add upper immediate to pc) | AUIPC | rd = (imm << 12) + PC |
|
||||
|
||||
| funct7+rs2+rs1+funct3+rd+opcode | funct7 | funct3 | opcode | 意义 |
|
||||
| ------------------------------- | ------- | ------ | ------ | ---------------------------------------- |
|
||||
| ADD rd, rs1, rs2 | 0000000 | ADD | OP | rd = rs1 + rs2 |
|
||||
| SLT rd, rs1, rs2 | 0000000 | STL | OP | rd = (rs1<rs2) ? 1 : 0 (用于有符号数) |
|
||||
| SLTU rd, rs1, rs2 | 0000000 | STLU | OP | rd = (rs1<rs2) ? 1 : 0 (用于无符号数);**注sltu rd, x0, rs的意思是rd = (rs != 0) ? 1 : 0;** |
|
||||
| AND rd, rs1, rs2 | 0000000 | AND | OP | rd = rs1 & rs2 |
|
||||
| OR rd, rs1, rs2 | 0000000 | OR | OP | rd = rs1 \| rs2 |
|
||||
| XOR rd, rs1, rs2 | 0000000 | XOR | OP | rd = rs1 ^ rs2 |
|
||||
| SLL rd, rs1, rs2 | 0000000 | SLL | OP | rd = rs1 << (rs2 & 0x1F ) |
|
||||
| SRL rd, rs1, rs2 | 0000000 | SRL | OP | rd = (unsigned) rs1 >> (rs2 & 0x1F) |
|
||||
| SUB rd, rs1, rs2 | 0100000 | SUB | OP | rd = rs1 - rs2 |
|
||||
| SRA rd, rs1, rs2 | 0100000 | SRA | OP | rd = (signed) rs1 >> (rs2 & 0x1F) |
|
||||
|
||||
NOP指令的编码:ADDI x0, x0, 0
|
||||
|
||||
##### 5. 控制转移指令(8条指令)
|
||||
|
||||
- 无条件跳转(U-type or J-type)
|
||||
| 指令 | 编码 | 意义 |
|
||||
| ----------------------------------------- | --------------------------------- | ----------------------------- |
|
||||
| JAL rd, imm(jump and link) | imm+rd+JAL(opcode) | rd = pc + 4; pc += imm; |
|
||||
| JALR rd, rs1, imm(jump and link register) | imm+rs1+0(funct3)+rd+JALR(opcode) | rd = pc + 4; pc += rs1 + imm; |
|
||||
|
||||
- 条件分支(S-type or B-type)
|
||||
| imm+rs2+rs1+funct3+imm+opcode | funct3 | opcode | 意义 |
|
||||
| ----------------------------- | -------- | ------ | ------------ |
|
||||
| BEQ rs1,rs2,offset | BEQ | BRANCH | if(rs1==rs2) |
|
||||
| BNE rs1,rs2,offset | BNE | BRANCH | if(rs1!=rs2) |
|
||||
| BLT/BLTU rs1, rs2, offset | BLT/BLTU | BRANCH | if(rs1<rs2) |
|
||||
| BGE/BGEU rs1, rs2, offset | BGE/BGEU | BRANCH | if(rs1>=rs2) |
|
||||
|
||||
##### 6. LOAD和STORE指令(8条指令)
|
||||
|
||||
| imm+rs1+funct3+rd+opcode | funct3 | opcode | 意义 |
|
||||
| ------------------------ | ------ | ------ | --------------- |
|
||||
| LW rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
|
||||
| LH rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
|
||||
| LHU rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
|
||||
| LB rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
|
||||
| LBU rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
|
||||
|
||||
| imm+rs2+rs1+funct3+imm+opcode | funct3 | opcode | 意义 |
|
||||
| ----------------------------- | ------ | ------ | ---------------- |
|
||||
| SW(32位) | 宽度 | STORE | mem(rs1+imm)=rs2 |
|
||||
| SH(16位) | 宽度 | STORE | mem(rs1+imm)=rs2 |
|
||||
| SB(8位) | 宽度 | STORE | mem(rs1+imm)=rs2 |
|
||||
|
||||
|
||||
|
||||
##### 7. 内存模型(2条指令)
|
||||
|
||||
本节已 **过时**,新的内存模型正在修订中。
|
||||
| imm[11:0] | rs1 | funct3 | rd | opcode | 意义 |
|
||||
| --------------------------------- | ------- | ------- | ------- | -------- | --------- |
|
||||
| imm[11:8]保留,imm[7:4]前续,imm[3:0]后续 | 保留,应置为0 | FENCE | 保留,应置为0 | MISC-MEM | 顺序化IORW访问 |
|
||||
| imm[11:0]保留,应置为0 | 保留,应置为0 | FENCE.I | 保留,应置为0 | MISC-MEM | 同步指令和数据流 |
|
||||
|
||||
|
||||
|
||||
|
||||
##### 8. 控制和状态寄存器指令(6条指令)
|
||||
- CSR指令
|
||||
|
||||
| csr+rs1+funct3+rd+opcode | rs1 | funct3 | opcode | 意义 |
|
||||
| ------------------------ | --------- | ------ | ------ | ---------------------------- |
|
||||
| csrrw rd, csr, rs1 | source | CSRRW | SYSTEM | tmp=rs1; rd=csr; csr=tmp |
|
||||
| csrrs rd, csr, rs1 | source | CSRRS | SYSTEM | tmp=rs1; rd=csr; csr\|=tmp |
|
||||
| csrrc rd, csr, rs1 | source | CSRRC | SYSTEM | tmp=rs1; rd=csr; csr &= !tmp |
|
||||
| csrrwi rd, csr, rs1 | uimm[4:0] | CSRRWI | SYSTEM | rd=csr; csr=uimm[4:0] |
|
||||
| csrrsi rd, csr, rs1 | uimm[4:0] | CSRRSI | SYSTEM | rd=csr; csr\|=uimm[4:0] |
|
||||
| csrrci rd, csr, rs1 | uimm[4:0] | CSRRCI | SYSTEM | rd=csr; csr\|=uimm[4:0] |
|
||||
|
||||
- 计时器和计数器(伪指令)
|
||||
|
||||
| csr+rs1+funct3+rd+opcode | csr | rs1 | funct3 | opcode | 意义 |
|
||||
| ------------------------ | ------------- | ---- | ------ | ------ | -------------- |
|
||||
| rdcycle[h] rd | RDCYCLE[H] | 0 | CSRRS | SYSTEM | 读取cycle CSR |
|
||||
| rdtime[h] rd | RDTIME[H] | 0 | CSRRS | SYSTEM | 读取time CSR |
|
||||
| rdinstrent[h] rd | RDINSTRENT[H] | 0 | CSRRS | SYSTEM | 读取instrent CSR |
|
||||
|
||||
##### 9. 环境调用和断点(2条指令)
|
||||
|
||||
| funct12 | rs1 | funct3 | rd | opcode | 意义 |
|
||||
| ------- | ---- | ------ | ---- | ------ | ------------ |
|
||||
| ECALL | 0 | PRIV | 0 | SYSTEM | 用于产生对执行环境的请求 |
|
||||
| EBREAK | 0 | PRIV | 0 | SYSTEM | 调试器用它返回调试环境 |
|
||||
|
||||
ECALL指令只是触发一个当前特权级下的exception,并不执行其它操作。
|
||||
|
||||
注:ECALL可以在各自的特权级下产生不同的exception,所以可以选择性地委派call exception。对于Unix-like操作系统来说,一个典型的用法是通过U-mode下的调用切换到S-mode。
|
||||
|
||||
#### 3. RV32E:RV32I的精简版,为嵌入式系统而设计
|
||||
|
||||
1. 程序员模型:通用寄存器从31个(x1~x31)减少到15个(x1~x15)
|
||||
2. 指令集:与RV32I相同,但计时器和计数器伪指令不是必须的。
|
||||
3. 扩展:可进行M,A,C扩展,只有两种特权级(用户模式和机器模式)
|
||||
|
||||
#### 4. RV64I:RV32I的变体,只描述与RV32I的不同之处。
|
||||
|
||||
1. 寄存器状态:寄存器扩展为64位,且支持64位用户地址空间。
|
||||
|
||||
2. 整数计算指令(增加了9条特有指令)
|
||||
|
||||
RV64I的指令长度也是32位的,操作的是64位数值。但也提供了变种指令来操作32位数值,这些指令的操作码后面都增加了后缀“W",是RV64I特有的指令。
|
||||
|
||||
| imm | rs1 | funct3 | rd | opcode |
|
||||
| --------- | ---- | ------ | ---- | --------- |
|
||||
| imm[11:0] | src | ADDIW | dest | OP-IMM-32 |
|
||||
| imm[4:0] | src | SLLIW | dest | OP-IMM-32 |
|
||||
| imm[4:0] | src | SRLIW | dest | OP-IMM-32 |
|
||||
| imm[4:0] | src | SRAIW | dest | OP-IMM-32 |
|
||||
|
||||
| funct7 | rs2 | rs1 | funct3 | rd | opcode |
|
||||
| ------- | ---- | ---- | --------- | ---- | ------ |
|
||||
| 0000000 | src2 | src1 | ADDW | dest | OP-32 |
|
||||
| 0000000 | src2 | src1 | SLLW/SRLW | dest | OP-32 |
|
||||
| 0100000 | src2 | src1 | SUBW/SRAW | dest | OP-32 |
|
||||
|
||||
|
||||
|
||||
3. Load和Store指令(增加了3条指令)
|
||||
|
||||
增加LD(64位),LWU(32位无符号),SD(64位)三条指令。
|
||||
|
||||
4. 系统指令:与RV32I完全相同,只是操作的对象换成了64位的。需要注意的是,伪指令rdcycle,rdtime,rdinstret分别读取的是相应寄存器的全部64位(cycle,time,instret计数器),所以rdcycleh,rdtimeh,rdinstreth在RV64I里就不需要了,是非法的。
|
||||
|
||||
#### 5. RV128I
|
||||
|
||||
#### 6. M:整数乘除扩展
|
||||
|
||||
1. 乘法操作
|
||||
|
||||
| funct7+rs2+rs1+funct3+rd+opcode | funct7 | funct3 | opcode | 意义 |
|
||||
| ------------------------------- | ------ | --------------- | ------ | ----------------------------------- |
|
||||
| MUL/MULH\[[S]U] rd, rs1, rs2 | MULDIV | MUL/MULH\[[S]U] | OP | MUL将乘法的低位放入目标寄存器,MULH则是放高位 |
|
||||
| MULW rd, rs1, rs2 | MULDIV | MULW | OP-32 | 仅用于RV64,计算源寄存器的低32位,再用MUL指令获取高32位的值 |
|
||||
|
||||
|
||||
|
||||
2. 除法操作
|
||||
|
||||
| funct7+rs2+rs1+funct3+rd+opcode | funct7 | funct3 | opcode | 意义 |
|
||||
| ------------------------------- | ------ | --------------- | ------ | ---------------------- |
|
||||
| DIV[U]/REM[U] rd, rs1, rs2 | MULDIV | DIV[U]/REM[U] | OP | rs1/rs2。DIV提供商,REM提供余数 |
|
||||
| DIV[U]W/REM[U]W rd, rs1, rs2 | MULDIV | DIV[U]W/REM[U]W | OP-32 | 仅用于RV64,源寄存器的低32位相除。 |
|
||||
|
||||
|
||||
|
||||
#### 7. A:原子扩展
|
||||
|
||||
原子指令是原子性地“读-改-写”内存,以支持同一内存空间中多个硬件线程之间的同步。共有两种形式的原子指令,load-reserved/store-conditional指令和原子“获取-操作”内存指令。它们都支持各种内存一致性排序,都支持RCsc内存一致性模型。
|
||||
|
||||
1. 原子指令的顺序
|
||||
|
||||
基本RISCV ISA使用的是宽松的内存模型,用FENCE指令来施加额外的次序约束。
|
||||
|
||||
为了更有效地支持一致性,每个原子指令都有两个位,aq和rl,用于指定其他的RISC-V harts所观察到的额外的内存排序约束。
|
||||
|
||||
如果两个比特都清空了,那么就不会对原子内存操作施加额外的排序约束。如果只设置了aq位,则将原子内存操作视为**获取访问**,即:在**获取访问**操作之前,在这个RISC-V hart中,没有任何后续的内存操作可以被观察到。如果只设置rl位,则将原子内存操作视为一个**释放访问**,即:在这个RISC-V hart中,任何早期内存操作之前,释放内存操作都不能观察到。如果aq和rl位都设置了,则在所有早期的内存操作之前或后期的内存操作之后,在相同的RISCV hart上,原子内存操作是顺序一致的且不能被观察到,而只能被全局次序中的其它hart观察到。
|
||||
|
||||
2. load-reserved/store-conditional指令
|
||||
|
||||
| funct5+aq+rl+rs2+rs1+funct3+rd+opcode | funct5 | rs2 | opcode | 意义 |
|
||||
| ------------------------------------- | ------ | ---- | ------ | ------------------------------------------------------------ |
|
||||
| lr rd, (rs1) | LR | 0 | AMO | 原子交换的前半部分:load且将内存地址注册为reservation |
|
||||
| sc rd, rs2, (rs1) | SC | src | AMO | 原子交换的后半部分:(rs1)=rs2如果内存地址的reservation有效,成功则rd为0,失败则rd非0. |
|
||||
|
||||
在完成上一个LR之前,一个SC指令不能被另一个RISC-V hart观察到。由于LR/SC序列的原子性,在LR和一个成功的SC之间,任何hart的内存操作都不能被观测到。LR/SC序列可以通过在SC指令上设置aq位来赋予**获取语义**。LR/SC序列可以通过在LR指令上设置rl位来赋予**释放语义**。在LR指令上设置aq和rl位,并在SC指令上设置aq位,使得LR/SC序列与其他顺序一致的原子操作顺序一致。
|
||||
|
||||
如果LR/SC的位均没有设置,则LR/SC序列是可以观测到的。这适用于并行下降操作。
|
||||
|
||||
3. 原子内存操作atomic memory operation(AMO)
|
||||
|
||||
| funct5+aq+rl+rs2+rs1+funct3+rd+opcode | funct5 | opcode | 意义 |
|
||||
| ------------------------------------- | ------------- | ------ | -------------------------- |
|
||||
| amoswap.w/d rd, rs2, (rs1) | AMOSWAP.W/D | AMO | rd=(rs1); |
|
||||
| amoadd.w/d rd, rs2, (rs1) | AMOADD.W/D | AMO | rd=(rs1);(rs1)=rd+rs2 |
|
||||
| amoand.w/d rd, rs2, (rs1) | AMOAND.W/D | AMO | rd=(rs1);(rs1)=rd&rs2 |
|
||||
| amoor.w/d rd, rs2, (rs1) | AMOOR.W/D | AMO | rd=(rs1);(rs1)=rd\|rs2 |
|
||||
| amoxor.w/d rd, rs2, (rs1) | AMOXOR.W/D | AMO | rd=(rs1);(rs1)=rd^rs2 |
|
||||
| amomax[u].w/d rd, rs2, (rs1) | AMOMAX[U].W/D | AMO | rd=(rs1);(rs1)=max(rd,rs2) |
|
||||
| amomin[u].w/d rd, rs2, (rs1) | AMOMIN[U].W/D | AMO | rd=(rs1);(rs1)=min(rd,rs2) |
|
||||
|
||||
原子内存操作(AMO)指令为多处理器同步执行"读-修改-写"操作,并使用r类型指令格式进行编码。这些AMO指令从rs1代表的内存地址原子地加载数据值,将该值放入rd,对rd的值和rs2的值进行操作,然后将结果存储回rs1的内存地址。AMOs可以在内存中操作64位(仅限RV64)或32位的数据。对于RV64,AMOs总是对rd中的值进行符号扩展。rs1中的地址必须与操作数的大小自然对齐(即,8字节对齐64位字,4字节对齐32位字),否则就会造成未对齐地址异常。
|
||||
|
||||
支持的操作有swap, add, and, or, xor, 符号和非符号整数的maximum和minimum。除了排序约束,这些AMOs也可以实现并行下降操作,一般会通过写入x0的方式来丢弃返回值。
|
||||
|
||||
为了帮助实现多处理器同步,AMOs可选地提供发布一致性语义。如果设置了aq位,那么在这个riscv - v hart中,就不能观察到在AMO之前发生的内存操作。相反,如果设置了rl位,那么其他的RISC-V harts将不会在这个riscv - v hart的内存访问之前观察AMO。
|
||||
|
||||
#### 8. F:浮点扩展
|
||||
|
||||
1. F寄存器
|
||||
|
||||
F扩展增加了32个浮点寄存器和1个浮点控制和状态寄存器fcsr。浮点寄存器是f0-f31,每个32位宽。浮点控制和状态寄存器则包含了浮点单元的操作模式和异常状态。
|
||||
|
||||
2. 浮点控制和状态寄存器
|
||||
|
||||
3. NaN的生成与传播
|
||||
|
||||
4. 低能的算术
|
||||
|
||||
5. 单精度Load和Store指令
|
||||
|
||||
6. 单精度浮点计算指令
|
||||
|
||||
7. 单精度浮点转换和移动指令
|
||||
|
||||
8. 单精度浮点比较指令
|
||||
|
||||
9. 单精度浮点类型指令
|
||||
|
||||
#### 9. D:双精度扩展
|
||||
|
||||
1. D寄存器
|
||||
|
||||
D将32个浮点寄存器扩展到64位,F寄存器现在可以保存32位或64位浮点值了。
|
||||
|
||||
2. NaN Boxing of Narrower Values
|
||||
|
||||
3. 双精度Load和Store指令
|
||||
|
||||
4. 双精度浮点计算指令
|
||||
|
||||
5. 双精度浮点转换和移动指令
|
||||
|
||||
6. 双精度浮点比较指令
|
||||
|
||||
7. 双精度浮点类型指令
|
||||
|
||||
#### 10. Q:四精度扩展
|
||||
|
||||
#### 11. L:十进制浮点扩展
|
||||
|
||||
#### 12. C:压缩指令扩展
|
||||
|
||||
#### 13. B:位操作扩展
|
||||
|
||||
#### 14. J:动态翻译语言扩展
|
||||
|
||||
#### 15. T:事物内存扩展
|
||||
|
||||
#### 16. P:封装的单指令多数据流扩展
|
||||
|
||||
#### 17. V:向量操作扩展
|
||||
|
||||
#### 18. N:用户级中断扩展
|
||||
|
||||
#### 19. G:RV32/64G 指令集列表
|
||||
|
||||
#### 20. RISC-V汇编程序员手册
|
||||
|
||||
- 寄存器在标准调用规约中的角色
|
||||
|
||||
| 寄存器 | ABI名字 | 描述 | 由谁保存 |
|
||||
| ------ | ----- | ------------- | ---- |
|
||||
| x0 | zero | 固定为0 | 无 |
|
||||
| x1 | ra | 返回地址 | 调用者 |
|
||||
| x2 | sp | 栈指针 | 被调用者 |
|
||||
| x3 | gp | 全局指针 | 无 |
|
||||
| x4 | tp | 线程指针 | 无 |
|
||||
| x5 | t0 | 临时寄存器/备用连接寄存器 | 调用者 |
|
||||
| x6-7 | t1-2 | 临时寄存器 | 调用者 |
|
||||
| x8 | s0/fp | 保存寄存器/桢指针 | 被调用者 |
|
||||
| x9 | s1 | 保存寄存器 | 被调用者 |
|
||||
| x10-11 | a0-1 | 函数参数/返回值 | 调用者 |
|
||||
| x12-17 | a2-7 | 函数参数 | 调用者 |
|
||||
| x18-27 | s2-11 | 保存寄存器 | 被调用者 |
|
||||
| x28-31 | t3-6 | 临时寄存器 | 调用者 |
|
||||
|
||||
- 普通伪指令
|
||||
|
||||
| 伪指令 | 对应的基本指令 | 意义 |
|
||||
| ---------------------------- | ---------------------------------------- | -------------------------- |
|
||||
| la rd, symbol | auipc rd, symbol[31:12] ; addi rd, rd, symbol[11:0] | rd = pc + symbol,载入地址 |
|
||||
| l{b\|h\|w\|d} rd, symbol | auipc rd, symbol[31:12]; l{b\|h\|w\|d} rd, symbol[11:0]\(rd) | 载入全局地址 |
|
||||
| s{b\|h\|w\|d} rd, symbol, rt | auipc rt, symbol[31:12]; s{b\|h\|w\|d} rd, symbol[11:0]\(rt) | 存储全局地址 |
|
||||
| nop | addi x0, x0, 0 | 空操作 |
|
||||
| li rd, immediate | 无穷序列 | rd = immediate,存储立即数 |
|
||||
| mv rd, rs | addi rd, rs, 0 | rd = rs + 0,复制寄存器 |
|
||||
| not rd, rs | xori rd, rs, -1 | rd = rs ^ -1, rs的补码 |
|
||||
| neg rd, rs | sub rd, x0, rs | rd = 0 - rs, 补码 |
|
||||
| negw rd, rs | subw rd, x0, rs | rd = 0 - rs, 补码 |
|
||||
| sext.w rd, rs | addiw rd, rs, 0 | rd = rs + 0, 符号扩展 |
|
||||
| seqz rd, rs | sltiu rd, rs, 1 | rd = (rs == 0) ? 1 : 0; |
|
||||
| snez rd, rs | sltu rd, x0, rs | rd = (rs != 0) ? 1 : 0; |
|
||||
| sltz rd, rs | slt rd, rs, x0 | rd = (rs < 0) ? 1 : 0; |
|
||||
| sgtz rd, rs | slt rd, x0, rs | rd = (rs > 0) ? 1 : 0; |
|
||||
| beqz rs, offset | beq rs, x0, offset | if (rs == 0), branch |
|
||||
| bnez rs, offset | bne rs, 0x, offset | if( rs != 0), branch |
|
||||
| blez rs, offset | bge x0, rs, offset | if(rs <= 0), branch |
|
||||
| bgez rs, offset | bge rs, x0, offset | if(rs >= 0), branch |
|
||||
| bltz rs, offset | blt rs, x0, offset | if(rs < 0), branch |
|
||||
| bgtz rs, offset | blt x0, rs, offset | if(rs > 0), branch |
|
||||
| bgt rs, rt, offset | blt rt, rs, offset | if(rs > rt), branch |
|
||||
| ble rs, rt, offset | bge rt,rs, offset | if(rs <= rt), branch |
|
||||
| bgtu rs, rt, offset | bltu rt, rs, offset | if( rs > rt), branch |
|
||||
| bleu rs, rt, offset | bgeu rt, rs, offset | if(rs <= rt), branch |
|
||||
| j offset | jal x0, offset | pc += offset |
|
||||
| jal offset | jal x1, offset | pc += offset; x1 = pc + 4; |
|
||||
| jr rs | jalr x0, rs, 0 | pc = rs + 0 |
|
||||
| jalr rs | jalr x1, rs, 0 | pc = rs + 0; x1 = pc + 4; |
|
||||
| ret | jalr x0, x1, 0 | pc = x1 + 0 |
|
||||
| call offset | auipc x6, offset[31:12]; jalr x1, x6, offset[11:0] | pc += offset; x1 = pc + 4; |
|
||||
| tail offset | auipc x6, offset[31:12]; jalr x0, x6, offset[11:0] | pc += offset |
|
||||
| fence | fence iorw, iorw | 隔离内存和IO |
|
||||
|
||||
|
||||
|
||||
- 访问CSR的伪指令
|
||||
|
||||
| 伪指令 | 对应的基本指令 | 意义 |
|
||||
| --------------- | ------------------------ | ------------------------- |
|
||||
| rdinstret[h] rd | csrrs rd, instret[h], x0 | rd = instret[h],读取指令退休计数器 |
|
||||
| rdcycle[h] rd | csrrs rd, cycle[h], x0 | rd = cycle[h],读取循环计数器 |
|
||||
| rdtime[h] rd | csrrs rd, time[h], x0 | rd = time[h], 读取实时时钟 |
|
||||
| csrr rd, csr | csrrs rd, csr, x0 | rd = csr, 读取csr |
|
||||
| csrw csr, rs | csrrw x0, csr, rs | csr = rs, 写入csr |
|
||||
| csrs csr, rs | csrrs x0, csr, rs | csr \|= rs, csr置位 |
|
||||
| csrc csr, rs | csrrc x0, csr, rs | csr &= !rs, csr清位 |
|
||||
| csrwi csr, imm | csrrwi x0, csr, imm | csr = imm, 立即数写入csr |
|
||||
| csrsi csr, imm | csrrsi x0, csr, imm | csr \|= imm, 立即数置位csr |
|
||||
| csrci csr, imm | csrrci x0, csr, imm | csr &= !imm, 立即数清位csr |
|
||||
|
||||
|
||||
|
||||
#### 21. 扩展RISC-V
|
||||
|
||||
#### 22. ISA子集命名约定
|
||||
|
||||
#### 23. 历史与致谢
|
||||
|
||||
### RISC-V手册下卷(riscv-privileged-v1.10)
|
||||
|
||||
#### 1. 介绍
|
||||
|
||||
1. 硬件平台术语
|
||||
|
||||
hart:硬件线程hardware thread。
|
||||
|
||||
核心(core):包含独立取指单元的组件。
|
||||
|
||||
协处理器(coprocessor):是与RISC-V核心相连的一个单元,且主要被RISC-V指令流序列化,但包含了一些额外的体系结构状态和指令集扩展,相对于主RISC-V指令流来说可能会有些有限的自治。
|
||||
|
||||
加速器(accelerator):一个非可编程的固定功能单元,或可以自治工作、但专门用于某项任务的核心。
|
||||
|
||||
2. 特权软件栈术语
|
||||
|
||||
AEE:应用程序执行环境
|
||||
|
||||
ABI:应用程序二进制接口
|
||||
|
||||
SBI:管理员二进制接口
|
||||
|
||||
SEE:管理员执行环境
|
||||
|
||||
HBI:管理程序二进制接口
|
||||
|
||||
HEE:管理程序执行环境
|
||||
|
||||
HAL:硬件抽象层
|
||||
|
||||
3. 特权级
|
||||
|
||||
| 级别 | 编码 | 名字 | 缩写 | 特点 |
|
||||
| ---- | ---- | -------- | ---- | ----------- |
|
||||
| 0 | 00 | 用户/应用 | U | 应用程序 |
|
||||
| 1 | 01 | 管理员 | S | 操作系统 |
|
||||
| 2 | 10 | Reserved | | |
|
||||
| 3 | 11 | 机器 | M | 必需的,可信的,最高级 |
|
||||
|
||||
4. 调试模式:用以支持片外调试或制造测试,可以认为是一种比机器级更高的特权级。
|
||||
|
||||
#### 2. 控制和状态寄存器(CSR)
|
||||
1. CSR地址映射约定
|
||||
|
||||
2. CSR列表
|
||||
|
||||
**用户级**:Trap Setup, Trap Handling, Floating-Point, Counter/Timers
|
||||
|
||||
**管理员级**:Trap Setup, Trap Handling, Protection & Translation
|
||||
|
||||
**机器级**:Information, Trap Setup, Trap Handling, Protection & Translation, Counter/Timers, Counter Setup
|
||||
|
||||
**调试级**:Debug/Trace(与机器级共享), Debug Mode
|
||||
|
||||
3. CSR域的说明
|
||||
|
||||
**WIRI**:写忽略读忽略,用于保留的只读域。
|
||||
|
||||
**WPRI**:写保留读忽略,用于保留的读写域。
|
||||
|
||||
**WLRL**:写合法读合法,用于部分位有用的保留域。
|
||||
|
||||
**WARL**:写任意读合法,用于部分位有用的保留域。
|
||||
|
||||
#### 3. 机器级ISA
|
||||
|
||||
##### 3.1机器级CSR
|
||||
|
||||
- **misa**:是一个WARL寄存器,它会报告hart支持的ISA。
|
||||
|
||||
| XLEN-1 ~ XLEN-2 | XLEN-3 ~ 26 | 25 ~ 0 |
|
||||
| --------------- | -------------- | ----------------------- |
|
||||
| MXL[1:0] (WARL) | WIRI | Extensions[25:0] (WARL) |
|
||||
| 2 bits | XLEN-28 (bits) | 26 bits |
|
||||
|
||||
**MXL**字段编码本机I指令的宽度,1代表32位,2代表64位,3代表128位。**Extensions**字段用单个字母占据1位的方式表明是否存在相应的标准扩展(比如A在0位,B在1位,Z在25位)。**I**表明RV32I或RV64I或RV128I,**E**表明RV32E,如果**I**和**E**同时存在应选择**I**。由于**G**和**IMAFD**重复,为避免冗余G表示除IMAFD外的额外扩展。**U**表示支持用户模式,**S**表示支持Supervisor模式。**X**表示存在非标准扩展。
|
||||
|
||||
- mvendorid:机器供应商ID寄存器。
|
||||
|
||||
- martid:机器架构ID寄存器。提供了hard的基本微架构的编码。
|
||||
|
||||
- mimpid:机器实现ID寄存器。提供了处理器实现版本的唯一编码。
|
||||
|
||||
- mhartid:包含了运行代码的硬件线程的id值,这个寄存器是只读的。在多处理器系统中,HardID的编号不一定的连续的,但一定有一个是0。
|
||||
|
||||
- **mstatus**:追踪和控制硬件线程的当前操作状态。
|
||||
|
||||
| 名称 | RV32 | RV64,RV128 | 意义 |
|
||||
| -------- | ------- | ----------- | ---------------------- |
|
||||
| UIE | 0 | 0 | 1开启用户中断 |
|
||||
| SIE | 1 | 1 | 1开启管理员中断 |
|
||||
| WPRI | 2 | 2 | 保留 |
|
||||
| MIE | 3 | 3 | 1开启机器中断 |
|
||||
| UPIE | 4 | 4 | 进入自陷前UIE的值 |
|
||||
| SPIE | 5 | 5 | 进入自陷前SIE的值 |
|
||||
| WPRI | 6 | 6 | 保留 |
|
||||
| MPIE | 7 | 7 | 进入自陷前MIE的值 |
|
||||
| SPP | 8 | 8 | 进入S-mode之前的特权级 |
|
||||
| WPRI | 9 ~ 10 | 9 ~ 10 | 保留 |
|
||||
| MPP[1:0] | 11 ~ 12 | 11 ~ 12 | 进入M-mode之前的特权级 |
|
||||
| FS[1:0] | 13 ~ 14 | 13 ~ 14 | |
|
||||
| XS[1:0] | 15 ~ 16 | 15 ~ 16 | |
|
||||
| MPRV | 17 | 17 | |
|
||||
| SUM | 18 | 18 | |
|
||||
| MXR | 19 | 19 | |
|
||||
| TVM | 20 | 20 | |
|
||||
| TW | 21 | 21 | |
|
||||
| TSR | 22 | 22 | |
|
||||
| WPRI | 23 ~ 30 | 23 ~ 31 | |
|
||||
| UXL[1:0] | | 32 ~ 33 | 同misa寄存器的MXL域 |
|
||||
| SXL[1:0] | | 34 ~ 35 | 同misa寄存器的MXL域 |
|
||||
| WPRI | | 36 ~ XLEN-2 | |
|
||||
| SD | 31 | XLEN -1 | |
|
||||
|
||||
**TSR,TW,TVW用来支持虚拟化** 。**TSR** (Trap SRET)支持拦截Supervisor异常返回指令,SRET。**TW** (Timeout Wait) 支持拦截WFI指令。**TVM**(Trap Virtual Memory) 支持拦截supervisor虚拟内存管理操作。当TVM=1时,在S模式下尝试读取或写入satp CSR或执行SFENCE.VMA指令会引发非法指令异常。当TVM=0时,这些操作在s模式下是允许的。**MPRV,MXR,SUM用来支持内存权限** 。**MXR** (Make eXecutable Readable)修改了加载访问虚拟内存的特权,当其为0时只有标记为可读的页面才可以加载,当其为1时从标记为可读或可执行的页面均可加载 。**SUM**(permit Supervisor User Memory access)修改S模式加载、存储和取指访问虚拟内存的特权。当其为0时,S模式不可以访问U模式的页面,当其为1时这样的访问是允许的。**MPRV** (Modify PRiVilege)修改在所有特权模式下加载和存储执行的权限级别,当其置位时加载和存储内存地址就好像当前特权模式为MPP一样,指令地址的转换和保护不受影响。如果不支持用户模式,则MPRV会被硬连接为1。**FS、XS和SD用来扩展上下文状态** 。**FS**字段编码浮点单元的状态,包括CSR fcsr和浮点数据寄存器f0-f31,**XS**字段编码额外的用户模式扩展和关联状态的状态。**SD**位是一个只读的位,它总结了FS字段或XS字段是否表示存在某个脏状态,那种状态需要将扩展的用户上下文保存到内存中。如果XS和FS都被固定为零,那么SD也总是为零。
|
||||
|
||||
- mtvec:机器自陷向量基址寄存器,记录自陷向量的配置,由向量基址和向量模式组成。
|
||||
|
||||
| 位 | 名称 | 作用 |
|
||||
| ---------- | -------------- | ------------------------------------------------------------ |
|
||||
| 2 ~ XLEN-1 | BASE[XLEN-1:2] | 中断例程的高XLEN-2位,要4字节对齐 |
|
||||
| 0 ~ 1 | MODE | 0中断入口仅由BASE决定,1异步中断的地址为BASE+4*cause,2和3为保留位 |
|
||||
|
||||
- medeleg和mideleg:机器自陷授权寄存器(机器异常授权寄存器和机器中断授权寄存器)
|
||||
|
||||
默认情况下,所有特权级的内陷都是在机器级处理的。为提高性能,使用medeleg和mideleg寄存器来表明某些异常和中断可以由较低的权限级别直接处理。在三种特权级都具备的系统中,在medeleg或mideleg中设置相应的位,会将内陷(S模式或U模式)授权给S模式内陷处理程序。
|
||||
|
||||
- mip,mie:mip的意思是有未处理的中断,mie的意思是中断使能。
|
||||
|
||||
sip/sie, uip/uie寄存器是mip/mie寄存器的受限视图。
|
||||
|
||||
| 位 | mip | mie | 意义 |
|
||||
| ------------ | ---- | ---- | ------------------------------------------------------------ |
|
||||
| 12 ~ XLEN-1 | WIRI | WPRI | 保留区 |
|
||||
| 11 | MEIP | MEIE | 外中断挂起或使能(机器级) |
|
||||
| 10 | WIRI | WPRI | 保留区 |
|
||||
| 9 | SEIP | SEIE | 外中断挂起或使能(管理员级) |
|
||||
| 8 | UEIP | UEIE | 外中断挂起或使能(用户级) |
|
||||
| 7 | MTIP | MTIE | 计时器中断挂起或使能(机器级,只读,只能通过写mtimecmp寄存器来清零) |
|
||||
| 6 | WIRI | WPRI | 保留区 |
|
||||
| 5 | STIP | STIE | 计时器中断挂起或使能(管理员级) |
|
||||
| 4 | UTIP | UTIE | 计时器中断挂起或使能(用户级) |
|
||||
| 3 | MSIP | MSIE | 软件中断挂起或使能(机器级) |
|
||||
| 2 | WIRI | WPRI | 保留区 |
|
||||
| 1 | SSIP | SSIE | 软件中断挂起或使能(管理员级) |
|
||||
| 0 | USIP | USIE | 软件中断挂起或使能(用户级) |
|
||||
|
||||
指令手册没有明确解释,但我**推测**外中断是外部设备引发的中断,软件中断是处理器和内存引发的中断,而计时器中断应属于外中断,它被单独分出来应该是基于计时器在进程调度中的重要作用。
|
||||
|
||||
如果一个中断是全局使能的,且mip和mie相应位被设置,则那个中断将会被获取。默认情况下,如果hart的当前特权级小于M,或者当前特权级是M且mstatus寄存器的MIE位被设置,则M模式下的中断是全局使能的。当mideleg相应位被设置,如果hart的当前特权级等于授权的特权级(U或S)且当前特权级的中断使能位被设置(mstatus寄存器的SIE或UIE位),或者当前特权级低于授权特权级,则中断被认为是全局使能的。
|
||||
|
||||
- mtime, mtimecmp
|
||||
|
||||
mtime是一个实时计数器,表现为内存映射的寄存器。不论是RV32, RV64还是RV128,mtime都是64位的。
|
||||
|
||||
mtimecmp是一个64位内存映射的计时器比较寄存器。当mtime中的值大于或等于mtimecmp时,将触发计时器中断。
|
||||
|
||||
- 硬件性能管理器
|
||||
|
||||
- **[m|h|s]counteren**:寄数器使能。将硬件性能监控计数器的可用性控制到下一个最低特权模式。
|
||||
|
||||
| 31 | ... | 3 | 2 | 1 | 0 |
|
||||
| ----- | ---- | ----- | ----- | ----- | ----- |
|
||||
| HPM31 | ... | HPM3 | IR | TM | CY |
|
||||
| 1 bit | ... | 1 bit | 1 bit | 1 bit | 1 bit |
|
||||
|
||||
**CY**代表cycle寄存器,**TM**代表time寄存器,**IR**代表instret寄存器(即指令消耗寄存器),**HPM**代表hpmcounter寄存器(即性能监控计数器)。当相应位清零后,在更低特权级下访问相应的寄存器会非法指令异常。
|
||||
|
||||
- mscratch:记录机器模式硬件线程上下文的地址,当进入机器模式下的中断例程时可以与用户寄存器切换
|
||||
|
||||
- mepc:机器异常程序计数器
|
||||
|
||||
- mcause:机器原因计数器
|
||||
|
||||
| 位 | 名称 | 作用 |
|
||||
| ----------- | -------------- | -------------------------------- |
|
||||
| XLEN-1 | Interrupt | 由中断引发则置1,由异常引发则置0 |
|
||||
| 0 ~ XLEN-2 | Exception Code | 引发中断的编码,详见下表 |
|
||||
|
||||
| 编码 | Interrupt位为1 | Interrupt位为0 |
|
||||
| ----- | ---------------- | ------------------- |
|
||||
| 0 | 用户软件中断 | 指令地址未对齐 |
|
||||
| 1 | 管理员软件中断 | 指令访问错误 |
|
||||
| 2 | 保留 | 非法指令 |
|
||||
| 3 | 机器软件中断 | 断点 |
|
||||
| 4 | 用户计时器中断 | Load地址未对齐 |
|
||||
| 5 | 管理员计时器中断 | Load访问错误 |
|
||||
| 6 | 保留 | Store/AMO地址未对齐 |
|
||||
| 7 | 机器计时器中断 | Store/AMO访问错误 |
|
||||
| 8 | 用户外中断 | U-mode下的环境调用 |
|
||||
| 9 | 管理员外中断 | S-mode下的环境调用 |
|
||||
| 10 | 保留 | 保留 |
|
||||
| 11 | 机器外中断 | M-mode下的环境调用 |
|
||||
| 12 | \>=12 保留 | 指令页错误 |
|
||||
| 13 | | Load页错误 |
|
||||
| 14 | | 保留 |
|
||||
| 15 | | Store/AMO页错误 |
|
||||
| \>=16 | | 保留 |
|
||||
|
||||
- mtval:机器陷阱值寄存器,当自陷发生在M-mode时,mtval通过写入异常信息来辅助软件处理这个自陷。它用来取代之前版本的mbadaddr寄存器。
|
||||
|
||||
##### 3.2 机器模式特权指令
|
||||
|
||||
| funct12 | rs1 | funct3 | rd | opcode | 意义 |
|
||||
| ------- | ---- | ------ | ---- | ------ | -------------------------------------- |
|
||||
| ECALL | 0 | PRIV | 0 | SYSTEM | 向更高特权级发起请求 |
|
||||
| EBREAK | 0 | PRIV | 0 | SYSTEM | 将控制转回调试环境 |
|
||||
| xRET | 0 | PRIV | 0 | SYSTEM | 在处理完自陷后返回,将xepc的值放到pc中 |
|
||||
| WFI | 0 | PRIV | 0 | SYSTEM | 等待中断 |
|
||||
|
||||
##### 3.3 Reset
|
||||
|
||||
一经重置,hart的特权模式就置为M。
|
||||
|
||||
##### 3.4 不可屏蔽中断Non-Maskable Interrupts(NMI)
|
||||
|
||||
不可屏蔽中断仅用于硬件错误的情况。
|
||||
|
||||
##### 3.5 物理内存特性 (**PMA**)
|
||||
|
||||
物理内存映射包含多种地址范围,一些对应到内存区域,一些对应到内存映射控制寄存器,一些则对应到地直空间的空洞。一些内存区域可能不支持读、写或执行,一些可能不支持子字或子块访问,一些可能不支持原子操作,一些可能不支持缓存一致性或可能有不同的内存模型。相似地,内存映射控制寄存器在访问宽度、对原子操作的支持和读写访问是否有副作用上也各不相同。在RISCV系统里,物理地址空间的这些属性和功能被称为物理内特性(PMA)。
|
||||
|
||||
PMAs是基础硬件的固有属性,在系统运行时基本不会改变。一些内存区域的PMAs在芯片设计时就已经确定,例如片上ROM。另一些是在电路板设计的时候确定的。芯片外总线也可能支持冷插拔或热插拔的设备。一些设备可能支持在运行的时候配置,这意味着不同的PMAs。
|
||||
|
||||
##### 3.6 物理内存保护 (**PMP**)
|
||||
|
||||
为了支持安全运行和抑制错误,最好限制运行在某个核上的程序对物理地址的访问。物理内存保护(PMP)单元提供了单个hart上机器模式下的控制寄存器,以设置在指定物理内存区域上的访问权限(read, write, execute)。PMP检查和PMA检查是并行的。
|
||||
|
||||
PMP访问控制的粒度是因平台而异的,即使在一个平台内也可能因物理内存区域而不同,但标准PMP编码支持小如4字节的区域。一些区域的特权级可以写死到硬件上,比如一些区域可以只在机器模式下可见。
|
||||
|
||||
当hart运行在S或U模式,mstatus寄存器的MPRV已置位且MPP域包含S或U,对于loads或stores来说PMP检查会应用于所有访问。PMP检查也适用于虚拟地址转换的页表访问,有效的特权模式为S。另外,PMP检查还可应用于M模式的访问,这种情况下PMP寄存器是锁定的,以便M模式下的软件也不可以修改它们。实际上,PMP可以将权限授予S或U模式,还可以从M模式撤销权限。
|
||||
|
||||
PMP违规总是精确地限制在处理器上。
|
||||
|
||||
- PMP CSRs
|
||||
|
||||
PMP入口由一个8位配置寄存器和一个XLEN位地址寄存器描述。一共支持16个PMP入口。任意PHP入口被实现之后,所有PMP CSRs都必须被实现。PMP CSRs只能在M模式下访问。
|
||||
|
||||
对于RV32,16个PMP入口的设置(pmp0cfg-pmp15cfg)保存在4个寄存器中pmpcfg0-pmpcfg3。对于RV64,16个入口保存在2个寄存器中pmpcfg0,pmpcfg2。
|
||||
|
||||
PMP地址寄存器是pmpaddr0-pmpaddr15。对于RV32,PMP地址寄存器编码34位物理地址的33-2位。对于RV64,PMP地址寄存器编码56位物理地址的55-2位。
|
||||
|
||||
配置寄存器的R,W,X位设置后,分别代表PMP入口允许读,写和指令执行。如下所示:
|
||||
|
||||
| L(WARL) | (WIRL) | A(WARL) | X(WARL) | W(WARL) | R(WARL) |
|
||||
| ------- | ------ | ------- | ------- | ------- | ------- |
|
||||
| 1 bit | 2 bits | 2 bits | 1 bit | 1 bit | 1 bit |
|
||||
|
||||
- 地址匹配
|
||||
|
||||
PMP配置寄存器的A字段编码了地址匹配的模式。如下表所示:
|
||||
|
||||
| A | 名称 | 描述 |
|
||||
| ---- | ----- | ------------------------------------------------ |
|
||||
| 0 | OFF | 空区域(无效,不匹配任何地址) |
|
||||
| 1 | TOR | Top Of Range |
|
||||
| 2 | NA4 | Naturally Aligned 4-byte region |
|
||||
| 3 | NAPOT | Naturally Aligned power-of-two region(>=8 bytes) |
|
||||
|
||||
NAPOT的对齐范围由对应地址寄存器的低位来决定。如下表所示:
|
||||
|
||||
| pmpaddr | pmpcfg.A | 匹配大小 |
|
||||
| ----------- | -------- | --------------------- |
|
||||
| aaaa...aaaa | NA4 | 4 byte NAPOT |
|
||||
| aaaa...aaa0 | NAPOT | 8 byte NAPOT |
|
||||
| aaaa...aa01 | NAPOT | 16 byte NAPOT |
|
||||
| ... | ... | ... |
|
||||
| a011...1111 | NAPOT | 2^(XLEN+1) byte NAPOT |
|
||||
| 0111...1111 | NAPOT | 2^(XLEN+2) byte NAPOT |
|
||||
|
||||
如果选择TOR,相关联的地址寄存器即为地址范围的顶部,前面的地址寄存器即为地址范围的底部。如果PMP入口i的A字段设置为TOR,则入口匹配任意地址a(pmpaddr ~i-1~ <= a < pmpaddr~i~)。当i为0时,a的范围为(a < pmpaddr~0~)。
|
||||
|
||||
- 锁定和特权模式
|
||||
|
||||
L位表示PMP入口被锁定。即,写配置寄存器和相关联的地址寄存器的操作将无效。只能通过重置系统的方法来解除锁定。另外,如果pmp*i*cfg.A被置为TOR,写pmpaddr*i-1*的操作将无效。
|
||||
|
||||
除了锁定PMP入口,L位还表示R/W/X权限是否是强制的。当L位为1时,这些权限将强制用于所有特权模式。当L位为0时,匹配PMP入口的所有M模式访问都被允许,R/W/X权限仅适用于S和U模式。
|
||||
|
||||
- 优先权与匹配逻辑
|
||||
|
||||
PMP入口的优先级是静态的。最低编号的PMP入口匹配任意字节的访问,它决定了访问是否成功。匹配的PMP入口必须匹配任意字节的访问,否则访问失败,这与L, R, W和X位无关。例如,如果PMP入口被设置为匹配4字节范围0xC-0xF,则对范围0x8-0xF的8字节访问将失败,假如那个PMP入口是匹配这些地址的最高优先级入口。
|
||||
|
||||
如果PMP入口匹配访问的所有字节,L、R、W和X位决定访问是否成功。
|
||||
|
||||
如果PMP入口匹配一个M模式访问,则此访问成功。
|
||||
|
||||
错误的访问将生成一个load,store或指令访问异常。
|
||||
|
||||
在某些实现里,未对齐的load, store和取指可能被分解为多个访问,在访问异常发生前其中的某些访问可能成功。
|
||||
|
||||
#### 4. 管理员级ISA
|
||||
|
||||
1. 管理员CSR:
|
||||
|
||||
- sstatus: 管理员状态寄存器,追踪处理器当前的操作状态,是XLEN位R/W寄存器。是mstatus寄存器的子集。
|
||||
|
||||
| 位 | 名称 | 意义 |
|
||||
| ----------- | ---- | -------------------------------------- |
|
||||
| XLEN-1 | SD | |
|
||||
| 20 ~ XLEN-2 | WPRI | 保留 |
|
||||
| 19 | MXR | |
|
||||
| 18 | SUM | |
|
||||
| 17 | WPRI | 保留 |
|
||||
| 15 ~ 16 | XS | |
|
||||
| 13 ~ 14 | FS | |
|
||||
| 9 ~ 12 | WPRI | 保留 |
|
||||
| 8 | SPP | 进入S-mode前hart的特权级。0:U, 1:S |
|
||||
| 6 ~ 7 | WPRI | 保留 |
|
||||
| 5 | SPIE | 进入S-mode前SIE的值。中断返回时写回SIE |
|
||||
| 4 | UPIE | 进入U-mode前UIE的值。中断返回时写回UIE |
|
||||
| 2 ~ 3 | WPRI | 保留 |
|
||||
| 1 | SIE | S-mode下的中断。1:使能,0:停止 |
|
||||
| 0 | UIE | U-mode下的中断。1:使能,0:停止 |
|
||||
|
||||
- UIE位开启或停止用户模式的中断。
|
||||
- UPIE位的功能和SPIE位类似。
|
||||
|
||||
- stvec:存放中断服务例程(ISR)的入口地址(BASE)和模式(MODE)
|
||||
|
||||
| XLEN-1 ~ 2 | 1 ~ 0 |
|
||||
| --------------------- | ----------- |
|
||||
| BASE[XLEN-1:2]\(WLRL) | MODE\(WARL) |
|
||||
|
||||
注意:BASE不包含最低两位,因为要求此地址4字节对齐,即最低两位一定是0。
|
||||
|
||||
- sip, sie:管理员中断寄存器,sip挂起中断,sie使能中断。它们是mip和mie的子集。
|
||||
|
||||
| 位 | sip | sie |
|
||||
| ----------- | -------------------------------- | -------------------------- |
|
||||
| 10 ~ XLeN-1 | WIRI(保留) | WPRI(保留) |
|
||||
| 9 | SEIP | SEIE |
|
||||
| 8 | UEIP:用户特权级的外部中断被挂起 | UEIE:使能用户级外部中断 |
|
||||
| 6 ~ 7 | WIRI(保留) | WPRI(保留) |
|
||||
| 5 | STIP | STIE |
|
||||
| 4 | UTIP | UTIE |
|
||||
| 2 ~ 3 | WIRI(保留) | WPRI(保留) |
|
||||
| 1 | SSIP:触发管理员级软件中断 | SSIE:使能管理员级软件中断 |
|
||||
| 0 | USIP | USIE |
|
||||
|
||||
这些位的名称都是4个字母的。第1个字母表示特权级:S,管理员级;U,用户级。第2个字母表示中断类型:E(External),外部中断;T(Timer),定时器中断;S(Software),软件中断。第4个字母表示挂起(P)还是使能(E)。
|
||||
|
||||
- time, cycle, instret :与用户级程序共用这些寄存器。
|
||||
|
||||
- scounteren:详见机器级ISA对应的寄存器。控制硬件性能监控计数器在U-mode下的可用性。1则可用,0则不可用。可控制的寄存器有:CY(cycle),TM(time), IR(instret), HPMn(hpmcountern).
|
||||
|
||||
- sscratch:记录管理员模式中断上下文的指针
|
||||
|
||||
- sepc:记录引起异常的指令的地址
|
||||
|
||||
- scause:各种中断的来源,interrupt位为0表示异常,为1表示中断
|
||||
|
||||
- stval:陷阱值寄存器。引发错误的内存地址会保存在这个寄存器中。
|
||||
|
||||
- satp:地址转换与内存保护寄存器。
|
||||
|
||||
| 字段 | RV32 | RV64 | 意义 |
|
||||
| ---- | --------------------- | ----------------------------- | -------- |
|
||||
| MODE | 1 bit (0:bare,1:Sv32) | 4 bits (0:bare,8:Sv39,9:Sv48) | 地址转换方案 |
|
||||
| ASID | 9 bits | 16 bits | 地址空间标识符 |
|
||||
| PPN | 22 bits | 44 bits | 根页表的物理页号 |
|
||||
|
||||
注意,修改satp寄存器并不意味着给页表更新和地址转换之间加上了次序约束。所以,在修改satp寄存器之前,应该先执行sfence.vma指令。
|
||||
|
||||
2. 管理员指令
|
||||
|
||||
除了SRET指令,还增加了一条新的管理员级的指令。
|
||||
|
||||
| funct7 | rs2 | rs1 | funct3 | rd | opcode | 意义 |
|
||||
| ---------- | ---- | ----- | ------ | ---- | ------ | ---------- |
|
||||
| SFENCE.VMA | asid | vaddr | PRIV | 0 | SYSTEM | 更新内存管理数据结构 |
|
||||
|
||||
3. Sv32:基于页的32位虚拟内存系统
|
||||
|
||||
当Sv32被写入satp寄存器的MODE域时,就进入了32位的分页虚拟内存模式。Sv32基于RV32,其被设计为足以支持类Unix操作系统。
|
||||
|
||||
- 寻址与内存保护
|
||||
|
||||
Sv32将虚拟地址映射到物理地址的过程与X86是相同的,页表的基址放在satp寄存器内。所不同的是Sv32的虚拟地址是32位的,而物理地址是34位的。虚拟地址可以寻址4G的空间,而物理地址可寻址16G的空间。我猜测这样设计是为了支持虚拟化,即虚拟机上的不同系统可以使用不同的物理内存,互不干扰。
|
||||
|
||||
| PPN[1] | PPN[0] | RSW | D | A | G | U | X | W | R | V |
|
||||
| ------- | ------- | ------ | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
|
||||
| 12 bits | 10 bits | 2 bits | 1 bit | 1 bit | 1 bit | 1 bit | 1 bit | 1 bit | 1 bit | 1 bit |
|
||||
|
||||
| 位 | 意义 |
|
||||
| ------- | ------------------------------------ |
|
||||
| V | 1代表PTE有效,0代表PTE无效 |
|
||||
| X, W, R | 可执行,可写,可读。全为0代表是指向下级页表的指针,否则就是叶子PTE |
|
||||
| U | 1代表U-mode可访问,0代表U-mode不可访问(仅用于叶子PTE) |
|
||||
| G | 全局映射 |
|
||||
| A | 虚拟页曾被访问过(仅用于叶子PTE) |
|
||||
| D | 虚拟页曾被写入过(仅用于叶子PTE) |
|
||||
| RSW | reserved |
|
||||
|
||||
- 虚拟地址转换过程
|
||||
|
||||
Sv32,Sv39,Sv48的转换算法都是一样的,不同的只有LEVELS和PTESIZE的值。LEVELS指的是vpn有几级,PTESIZE指的是PTE占几个字节。
|
||||
|
||||
4. Sv39:基于页的39位虚拟内存系统
|
||||
|
||||
Sv39支持39位的虚拟地址空间,其设计遵循Sv32的总体方案,本节仅介绍它们之间的不同点。Sv39基于RV64。
|
||||
|
||||
- 寻址与内存保护
|
||||
|
||||
Sv39的虚拟地址是64位的,但只有低39位有效,且高25位必须和第38位相等(注:这里说的第38位指的是从0开始计数)。从虚拟地址到物理地址共需3级页表的转换。
|
||||
|
||||
每一级的PTE都有可能是叶子PTE,所以除了4K的页外,还有可能存在2M或1G的页,每种页都必须依照它自己的大小在虚拟层面和物理层面对齐。
|
||||
|
||||
5. Sv48:基于页的48位虚拟内存系统
|
||||
|
||||
Sv48支持48位的虚拟地址空间,用以弥补Sv39的不足。Sv48也基于RV64。支持Sv48的实现也应当支持Sv39。
|
||||
|
||||
- 寻址与内存保护
|
||||
|
||||
Sv48的虚拟地址占用了64位,但只有低48位是有效的,且高16位都必须和第47位相等(注,第47位的意思是从0开始计数,如果从1开始计数则是第48位)。从虚拟地址到物理地址共需4级页表的转换。
|
||||
|
||||
每一级的PTE都有可能是叶子PTE,所以除了4K的页外,还可能有2M,1G,512G的页,每种页都必须依照它自己的大小在虚拟层面和物理层面对齐。
|
||||
|
||||
#### 5. Hypervisor扩展
|
||||
|
||||
#### 6. RISC-V特权指令列表
|
||||
|
||||
都是I-type类型的指令,它们是**环境调用和断点指令**`ecall`和`ebreak`,**陷阱返回指令**`uret`,`sret`,`mret`,**中断管理指令**`wfi`,**内存管理指令**`sfence.vma`。
|
||||
|
||||
#### 7. 平台级中断控制器(PLIC)
|
||||
|
||||
#### 8. 机器配置描述
|
||||
|
||||
#### 9. 历史
|
|
@ -0,0 +1,59 @@
|
|||
- 指令类型
|
||||
- R型:register型,从寄存器中取出源操作数,运算结果再写入寄存器中
|
||||
ADDU,ADD,CMP,MFIH,MFPC,MOVE
|
||||
- I型:immediate型 ,使用立即数作为源操作数
|
||||
ADDIU,ADDIU3,ADDSP3,ADDSP,CMPI,INT,LI,LW,LW_SP
|
||||
- B型:branch型,转移型
|
||||
B,BEQZ,BNEZ,BTEQZ,BTNEZ
|
||||
- J型:jump型,长跳转型
|
||||
JALR,JR,JRRA
|
||||
- 功能分类
|
||||
- 算术/逻辑运算
|
||||
1. `ADDIU rx immediate` (add immediate unsigned)
|
||||
2. `ADDIU3 rx ry immediate`
|
||||
3. `ADDSP3 rx immediate`
|
||||
4. `ADDSP immediate`
|
||||
5. `ADDU rx ry rz`
|
||||
40. `SUBU rx ry rz`
|
||||
26. `NEG rx ry` (negative)
|
||||
12. `CMP rx ry`
|
||||
13. `CMPI rx immediate`
|
||||
6. `AND rx ry`
|
||||
29. `OR rx ry`
|
||||
27. `NOT rx ry`
|
||||
44. `XOR rx ry`
|
||||
30. `SLL rx ry immediate`
|
||||
31. `SLLV rx ry`
|
||||
32. `SLT rx ry`
|
||||
33. `SLTI rx immediate`
|
||||
34. `SLTU rx ry`
|
||||
35. `SLTUI rx immediate`
|
||||
36. `SRA rx ry immediate`
|
||||
37. `SRAV rx ry`
|
||||
38. `SRL rx ry immediate`
|
||||
39. `SRLV rx ry`
|
||||
- 分支操作
|
||||
7. `B immediate`
|
||||
8. `BEQZ rx immediate`
|
||||
9. `BNEZ rx immediate`
|
||||
10. `BTEQZ immediate`
|
||||
11. `BTNEZ immediate`
|
||||
14. `INT immediate`
|
||||
15. `JALR rx` (jump and link)
|
||||
16. `JR rx` (jump register)
|
||||
17. `JRRA` (jump register RA)
|
||||
- 加载和存储
|
||||
18. `LI rx immediate` (load immediate)
|
||||
19. `LW rx ry immediate`(load indirect addressing)
|
||||
20. `LW_SP rx immediate`
|
||||
41. `SW rx ry immediate`
|
||||
42. `SW_RS immediate`
|
||||
43. `SW_SP rx immediate`
|
||||
- 数据传送
|
||||
21. `MFIH rx` (move from IH)
|
||||
22. `MFPC rx` (move from PC)
|
||||
23. `MOVE rx ry`
|
||||
24. `MTIH rx`
|
||||
25. `MTSP rx`
|
||||
- 空操作
|
||||
28. `NOP` (空操作)
|
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 725 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 169 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 214 KiB |
|
@ -0,0 +1,7 @@
|
|||
记录编译错误及解决方法
|
||||
|
||||
#### Error: Instruction csrw requires absolute expression
|
||||
|
||||
- 错误原因:这是由于编译器版本不正确引起的,由于riscv指令集尙处于发展阶段,所以不同版本的指令集可能是不一样的
|
||||
- 解决方法:重新编译工链,要使用指令集对应的编译器版本号
|
||||
|
|
@ -0,0 +1,359 @@
|
|||
## 资源
|
||||
|
||||
本文是对Intel手册的总结,但要注意在gcc下的汇编是使用的是gas syntax。
|
||||
|
||||
## 一、应用程序编程
|
||||
|
||||
### 1. 基本编程模型
|
||||
|
||||
#### 1.1 寄存器
|
||||
|
||||
- 通用寄存器(8个):
|
||||
|
||||
EAX,EBX,ECX,EDX,EBP,ESP,EBI,EDI
|
||||
|
||||
- 段寄存器(6个):
|
||||
|
||||
CS: DS: ES:SS: FS: GS:
|
||||
|
||||
- 标志寄存器:EFLAGS,低16位叫FLAGS,FLAGS是为8086和80286模式准备的。标志可以分为三种:状态标志,控制标志和系统标志
|
||||
|
||||

|
||||
|
||||
- 寄存器与内存的对应关系
|
||||
|
||||

|
||||
|
||||
###
|
||||
|
||||
### 2. 应用程序指令集
|
||||
|
||||
#### 2.0 AT&T汇编语法
|
||||
* (%eax) //代表eax指向的内存地址
|
||||
1(%eax) //指向eax指向的内存地址的下一个字节的地址
|
||||
* 赋值方向是从左向右(Intel语法的赋值方向是从右向左)
|
||||
#### 2.1 数据传送指令
|
||||
|
||||
- MOV:movq(moves a quadword from source to destination, in x86 a word is 16 bits), movl(moves a long from source to destination, long is 32 bits)
|
||||
- XCHG:(Exchange)交换两个操作数的内容。这两个操作数可以都是寄存器,也可以一个寄存器一个内存。当有一个操作数是内存时,LOCK信号会自动激活。
|
||||
- stos:将EAX的值保存到ES:DSI指向的字符串中。
|
||||
|
||||
#### 2.2 二进制运算指令
|
||||
* bt %1,%2 //bit test
|
||||
bts %1,%2 //把%2对应地址的%1位填入CF位,然后把%2对应地址的%1位置1(bit test and set)
|
||||
btr %1,%2 //把%2对应地址的%1位填入CF位,然后把%2对应地址的%1位置0(bit test and reset)
|
||||
btc %1,%2 //把%2对应地址的%1位填入CF位,然后把%2对应地址的%1位反转(bit test and complement)
|
||||
#### 2.3 十进制运算指令
|
||||
#### 2.4 逻辑运算指令
|
||||
#### 2.5 控制转移指令
|
||||
##### rep
|
||||
- repne: zf=0且cx>0,则重复
|
||||
* repe: zf=1且cx>0,则重复
|
||||
* rep: cx>0,则重复
|
||||
##### iret
|
||||
iret指令的执行过程(iret指令用于从int指令返回)
|
||||
|
||||
1. 从内核栈出弹出相应寄存器的值
|
||||
2. 如果存在特权级转换,则会从内核栈中弹出用户态栈的ss:esp
|
||||
3. 如果有错误码,需要软件完成错误码弹出的功能
|
||||
|
||||
##### int
|
||||
- int指令的执行过程
|
||||
1. 从IDT中获得描述符
|
||||
2. 检查%cs的域CPL <= 描述符记录的特权级DPL(这使得内核可以禁止一些特权级系统调用). 如果目标段选择符的PL<CPL(即发生了特权级转换),就在CPU内部的寄存器中保存%esp和%ss的值。
|
||||
4. 从一个任务段描述符中加载%ss和%esp。(不能使用用户栈来保存值,因为用户可能还没有建立合适的栈,所以使用任务段中指定的栈,这个栈是在内核模式中建立的)
|
||||
5. 将%ss压栈。
|
||||
6. 将%esp压栈。
|
||||
7. 将%eflags压栈。
|
||||
8. 将%cs压栈。
|
||||
9. 将%eip压栈。
|
||||
10. 清除%eflags的一些位。
|
||||
11. 设置%cs和%eip为描述符中的值。
|
||||
* INT指令通过软件方法调用中断处理程序,立即数(0~255)是IDT中要调用的中断例程的索引号。在保护模式下,IDT是8字节描述符的数组,描述符必须指明是中断、陷阱还是任务门。在实模式下,IDT是4字节大小的指针的数组。不管是实模式还是保护模式,IDT的基址的线性地址都由IDTR保存。
|
||||
|
||||
* INTO指令与INT指令相同,只是它的立即数被隐式地指定为4,此中断只有在溢出标志位设置为1时才使用。
|
||||
|
||||
* INT指令表现的像是远程调用(far call),只是在压入返回地址之前,标志寄存器也会被压入栈中。中断例程通过IRET指令返回,IRET指令会把栈里的标志寄存器和返回地址弹出。
|
||||
|
||||
* 在实模式下,INT指令会依次将标志寄存器,CS寄存器,返回地址压入栈中,然后跳转到由中断号指定的长地址处。
|
||||
#### 2.6 串与字符转移指令
|
||||
* ins:串输入指令
|
||||
* edx存放接口,edi存放内存指针
|
||||
insl:将edx指向的接口的数据放到edi指向的内存中
|
||||
#### 2.7 块结构语言的操作指令
|
||||
#### 2.8 标志控制指令
|
||||
* cld:将标志寄存器flag的DF位(方向标志位)清零,使变址寄存器SI,DI自动增1
|
||||
* std:将DF位置1,使SI,DI自动减1
|
||||
#### 2.9 协处理器接口指令
|
||||
#### 2.10 段寄存器指令
|
||||
* pushal 将所有通用寄存器的内容压入栈中,入栈顺序eax,ecx,edx,ebx,esp,ebp,esi,edi
|
||||
#### 2.11 其它指令
|
||||
* invlpg m // 清空TLB
|
||||
* SGDT/SIDT 将描述符表寄存器中的内容复制到由操作数定义的6字节的内存中。寄存器的LIMIT域赋值给有效地址的第一个字中。如果操作数是32位的,则接下来的3个字节由寄存器的BASE域赋值,第4个字节会写入0,(因为)最后一个字节未定义。否则,如果操作数是16位的,接下来的4个字节由寄存器的32位BASE域赋值。
|
||||
## 二、系统编程
|
||||
|
||||
### 1. 系统架构
|
||||
|
||||
### 2. 内存管理
|
||||
|
||||
|
||||
|
||||
### 3. 保护模式
|
||||
|
||||
保护模式提供了虚拟内存管理。
|
||||
|
||||
#### 3.1 寄存器
|
||||
|
||||

|
||||
|
||||
* 保护模式下新增的寄存器:
|
||||
|
||||
* GDTR全局描述符表寄存器(48位=32位基址+16位限长)
|
||||
|
||||
* LDTR局部描述符表寄存器
|
||||
|
||||
* TR任务寄存器
|
||||
|
||||
TR用于任务切换,存放了一个16位的选择符用以指示GDT中任务状态段描述符(TSS)的位置。TSS描述符定义了任务状态段的起始地址和大小。每个任务都有自己的TSS,存放进程运行时所需的硬件上下文。
|
||||
|
||||
* CR0~CR3四个控制寄存器
|
||||
|
||||
| 寄存器名 | 意义 |
|
||||
| -------- | ------------------------------------------------------------ |
|
||||
| CR3 | page directory base register(PDBR), store phisical address of the current page directory. |
|
||||
| CR2 | 导致页错误的线性地址 |
|
||||
| CR1 | 保留不用 |
|
||||
| CR0 | 控制协处理器和保护控制 |
|
||||
|
||||
| CR0的位 | 意义 |
|
||||
| ------- | ------------------------------------------------------------ |
|
||||
| PG | 1开启分页,0关闭分页。 |
|
||||
| PE | 1开启保护模式,0处于实模式。注:进入保护模式后就无法再回到实模式。 |
|
||||
| MP | 1系统中存在一个数学协处理器,0系统中不存在数学协处理器。 |
|
||||
| EM | 1系统使用软件模拟器执行数学运算。 |
|
||||
| R | 1表示安装的是80387数学协处理器。 |
|
||||
| TS | 任务切换位,由处理器自动设置,也可由软件清零。 |
|
||||
|
||||
* EIP的长度扩展到32位
|
||||
|
||||
* EFLAGS有32位,比实模式下的16位FLAGS新增了5个位。**IOPL**输入输出特权级占2位,指示执行IO操作时需要的特权级。**NT**嵌套任务标志,指明当前任务是否嵌套,由硬件自动设置,用软件清零。**RF**为恢复标志。**VM**为虚拟8086标志。
|
||||
|
||||
* CS,DS,ES,SS,FS,GS功能改变
|
||||
|
||||
实模式下的段寄存器在保护模式下被称为段选择符寄存器,里面存放的值不再是段基址而是段选择符。选择符用来选择位于GDT或LDT中的一个段描述符。段选择符格式如下:
|
||||
|
||||
| 15~3 | 2 | 1~0 |
|
||||
| ----- | ---------------------- | --------------- |
|
||||
| index | TI(1访问GDT,0访问LDT) | RPL(请求特权级) |
|
||||
|
||||
* 全局描述符表GDT
|
||||
|
||||
GDT中放的是段描述符,每个段描述符占8个字节。GDT最大尺寸为0x10000个字节,最多可放0x2000个表。
|
||||
|
||||
* 局部描述符表LDT
|
||||
|
||||
LDT中放的是段描述符。
|
||||
|
||||
#### 3.2 段描述符
|
||||
|
||||
段描述符格式如下:
|
||||
|
||||
| 字节 | 意义 |
|
||||
| :--: | :--------------------: |
|
||||
| 7 | 段基址31~24 |
|
||||
| 6 | G+X+O+AVL+段限长19~16 |
|
||||
| 5 | P+DPL+S+类型+A |
|
||||
| 4 | 段基址23~16 |
|
||||
| 3 | 段基址15~8 |
|
||||
| 2 | 段基址7~0 |
|
||||
| 1 | 段限长15~8 |
|
||||
| 0 | 段限长7~0 |
|
||||
|
||||
- 第6个字节说明了描述符的属性
|
||||
- G: GRANULARITY,1段长度以页(4K)为单位,0段长度以字节为单位。
|
||||
- X:1该段为32位代码段,0该段为16位代码段。
|
||||
- AVL: AVAILABLE FOR USE BY SYSTEMS PROGRAMMERS。
|
||||
- 第5个字节说明了描述符的访问权限
|
||||
- P: SEGMENT PRESENT,1段不在内存中,0段在内存中
|
||||
- DPL: DESCRIPTOR PRIVILEGE LEVEL,描述符特权级
|
||||
- S: 代码段或数据段描述符,0系统段或门描述符
|
||||
- A: ACCESSED,1访问过,0未访问过
|
||||
|
||||
#### 3.3 地址映射
|
||||
|
||||
1. 寻址与内存保护
|
||||
|
||||

|
||||
|
||||
2. 虚拟地址转换过程
|
||||
|
||||

|
||||
|
||||
#### 3.4 保护机制
|
||||
|
||||
四种特权级如下所示,大多数操作系统只用0和3两个特权级:
|
||||
|
||||
| 编号 | 特权级 |
|
||||
| ------ | -------- |
|
||||
| ring 0 | 内核 |
|
||||
| ring 1 | 系统调用 |
|
||||
| ring 2 | 共享库 |
|
||||
| ring 3 | 应用程序 |
|
||||
|
||||
在硬件支持下,通过软件对存储器访问进行检查和限制的防护措施称为**保护**。
|
||||
|
||||
- 保护性检查
|
||||
|
||||
保护性检查是由MMU硬件完成的,检查项目如下:
|
||||
|
||||
- 类型检查:检查存放段选择符的段寄存器与要访问的段描述符类型是否一致。
|
||||
- 限长检查:检查虚拟地址是否在段限长之内,以确保该段的访问没有越界。
|
||||
- 指令集的限制:有16条指令只能在特权级为0的代码段中执行。
|
||||
- I/O限制:I/O指令只能在小于等于IOPL的特权级上执行。
|
||||
- LDT的限制:每个任务的LDT都只能自己使用而拒绝其它任务使用。
|
||||
- 越级调用限制:CALL选择符-->门描述符-->段描述符
|
||||
|
||||
- 越级访问代码段和数据段
|
||||
|
||||
若要从低特权级访问高特权级内存段中的的代码和数据,需要使用门描述符。
|
||||
|
||||
|
||||
### 4. 多任务
|
||||
|
||||
80386没有为多任务处理添加新的特殊指令,而是使用了特殊的数据结构。支持多任务处理的数据结构和寄存器是:任务状态段(Task state segment),任务状态段描述符(Task state segment descriptor),任务寄存器(Task register),任务门描述符(Task gate descriptor)。
|
||||
|
||||
### 5. 输入输出
|
||||
|
||||
#### 5.1 I/O地址映射
|
||||
|
||||
- 80386允许两种方式的I/O:I/O地址空间(使用特殊I/O指令),I/O内存映射(无需特殊指令)
|
||||
|
||||
- I/O地址空间
|
||||
|
||||

|
||||
|
||||
- I/O地址空间的0页(0000H~00FFH)是主板上I/O芯片的接口地址,共计256字节的寻址区。这些芯片是计时器/计数器、中断控制器、DMA控制器、并行接口和键盘控制器。
|
||||
- 其它的I/O地址空间(0100H~FFFFH)用于主板扩展槽上的接口控制卡(也叫适配器)。如图形适配器、声卡、网卡等。
|
||||
|
||||
### 6. 异常与中断
|
||||
|
||||
中断来自于CPU外部的异步事件,而异常来自于CPU自身在指令执行过程中检测到的条件。
|
||||
|
||||
- 有两种中断:可屏蔽,不可屏蔽。
|
||||
- 有两种异常:由处理器检测的(包括faults, traps, aborts),可编程的(指令INTO, INT 3, INT n, BOUND都能触发异常,这些异常也被叫做“软件中断”,但处理器也把它们作为异常来处理)。
|
||||
|
||||
#### 6.1 未定义
|
||||
|
||||
|
||||
|
||||
#### 6.2 IDT与IDTR
|
||||
|
||||
- IDTR中断描述符表寄存器(48位=32位基址+16位限长)
|
||||
|
||||
LIDT指令:将内存中IDT长度及起始地址加载到IDTR
|
||||
SIDT指令:将IDTR的内容加载到内存中
|
||||
|
||||
- 中断描述符表IDT
|
||||
|
||||
IDT中放的是中断门描述符(即中断处理程序的入口),IDT中有256个门描述符,每个门描述符占8个字节。
|
||||
|
||||
有4种类型的门描述符如下表所示:
|
||||
|
||||
| 门描述符类型 | 作用 |
|
||||
| -------------- | ------------------------------------------ |
|
||||
| 调用门 | 从当前特权级到更高特权级的间接转移 |
|
||||
| 任务门 | 将控制传给请求特权级比当前特权级高的任务 |
|
||||
| 中断门和陷阱门 | 将控制传给当前任务的高特权级的其它服务程序 |
|
||||
|
||||
门描述符如下表所示(5字节的0位:1陷阱,0中断):
|
||||
|
||||
| 字节 | 任务门 | 中断门/陷阱门 | 调用门 |
|
||||
| ---- | -------------- | -------------- | -------------- |
|
||||
| 7 | 未使用 | offset[31:24] | offset[31:24] |
|
||||
| 6 | 未使用 | offset[23:16] | offset[23:16] |
|
||||
| 5 | P+DPL+TYPE | P+DPL+TYPE | P+DPL+TYPE |
|
||||
| 4 | 未使用 | 000+未使用 | 000+双字计数 |
|
||||
| 3 | selector[15:8] | selector[15:8] | selector[15:8] |
|
||||
| 2 | selecotr[7:0] | selecotr[7:0] | selecotr[7:0] |
|
||||
| 1 | 未使用 | offset[15:8] | offset[15:8] |
|
||||
| 0 | 未使用 | offset[7:0] | offset[7:0] |
|
||||
|
||||
门描述符里各位的意义如下表所示:
|
||||
|
||||
| 位 | 意义 |
|
||||
| --------------- | ------------------------------------------ |
|
||||
| P(1 bit) | 1描述符有效,0描述符无效 |
|
||||
| DPL(2 bit) | 描述符特权级 |
|
||||
| TYPE(5 bit) | 0xC调用门,0x9任务门,0xE中断门,0xF陷阱门 |
|
||||
| 双字计数(5 bit) | 只用在调用门中 |
|
||||
|
||||
|
||||
#### 6.3 硬件中断的产生
|
||||
|
||||
```
|
||||
1, CPU查询中断控制器(8259A),读取中断向量
|
||||
2, CPU从IDT中查中断向量对应的描述符,读取段选择子
|
||||
3, CPU通过段选择子从GDT中取得段描述符,跳转到中断服务例程的地址
|
||||
4, CPU通过CPL、DPL判断是否发生特权级转换,并决定是否进行内核栈和用户栈之间的转换。
|
||||
5, CPU将被打断程序的寄存器保存在内核栈中
|
||||
6, CPU将中断例程的地址加载到cs:eip寄存器中,开始执行中断例程
|
||||
```
|
||||
|
||||
#### 6.4 硬件中断的结束
|
||||
|
||||
```
|
||||
1, 从内核栈出弹出相应寄存器的值
|
||||
2, 如果存在特权级转换,则会从内核栈中弹出用户态栈的ss:esp
|
||||
3, 如果有错误码,需要软件完成错误码弹出的功能
|
||||
```
|
||||
|
||||
#### 6.5 中断汇总
|
||||
|
||||
前32个中断是由Intel保留由系统使用的,即软件异常,比如除0错误和访问非法的内存页。
|
||||
|
||||
| 中断号 | 意义 |
|
||||
| --------- | -------------------- |
|
||||
| 0 | 除0 |
|
||||
| 1 | 调试 |
|
||||
| 2 | 不可屏蔽中断 |
|
||||
| 3 | 断点 |
|
||||
| 4 | 溢出 |
|
||||
| 5 | 边界检查 |
|
||||
| 6 | 无效操作码,非法指令 |
|
||||
| 7 | 协处理器不可用 |
|
||||
| 8 | double fault |
|
||||
| 9 | 保留(486后不再使用) |
|
||||
| 10 | 无效的任务切换段 |
|
||||
| 11 | 段不存在 |
|
||||
| 12 | 栈异常 |
|
||||
| 13 | 通用保护异常 |
|
||||
| 14 | 页错误 |
|
||||
| 15 | 保留 |
|
||||
| 16 | 协处理器错误 |
|
||||
| 17 ~ 31 | 保留 |
|
||||
| 32 ~ 255 | `INTR pin`外部中断 |
|
||||
|
||||
### 7. 初始化
|
||||
|
||||
### 8. 协处理器和多处理器
|
||||
|
||||
### 9. 调试
|
||||
|
||||
## 三、兼容性
|
||||
|
||||
### 1. 执行80286保护模式的代码
|
||||
|
||||
### 2. 实模式
|
||||
|
||||
实模式是用16位的寄存器访问20位的物理地址,这是由CPU中的MMU单元自动完成的。
|
||||
|
||||
### 3. 虚拟8086模式
|
||||
|
||||
### 4. 16位与32位代码的混合
|
||||
|
||||
## 四、指令集
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
#### long mode
|
||||
|
||||
长模式是指把寄存器扩展为64位,添加了8个整数寄存器和8个SSE寄存器。线性地址扩展到64位,物理地址扩展到52位。从本质上讲,长模式是给CPU增加了一个新的模式。
|
||||
|
||||
长模式不支持硬件任务切换或虚拟8086模式。长模式下由CS决定代码运行状态:64位(真正的长模式),32位或16位(兼容模式)。分页是强制使用的,由于性能原因分段已经被简化了。长模式下的分段是平直模型(a flat model),除了两个寄存器:FS和GS。可以通过MSR寄存器里的FS.base和GS.base来设置这两个段寄存器的基址。
|
||||
|
||||
|
||||
|
||||
#### 寄存器
|
||||
|
||||
##### 通用寄存器
|
||||
|
||||
`rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp, r8~r15`
|
||||
|
||||
##### 指针寄存器
|
||||
|
||||
`rip`
|
||||
|
||||
##### 段寄存器
|
||||
|
||||
`cs, ds, ss, es, fs, gs `
|
||||
|
||||
##### 标志寄存器
|
||||
|
||||
`rflags`
|
||||
|
||||
##### 控制寄存器
|
||||
|
||||
`cr0, cr2, cr3, cr4, cr8`
|
||||
|
||||
- cr4寄存器各个位的意义。
|
||||
|
||||
| 位 | 标签 | 描述 |
|
||||
| ---- | ---- | ------------------------------------------------------------ |
|
||||
| ... | | |
|
||||
| 4 | PSE | Page Size Extension,1:允许每个页为4M,0:每个页只能是4K |
|
||||
| 5 | PAE | Physical Address Extension,1:允许使用32位以上物理地址,0:只能使用32位物理地址 |
|
||||
| ... | | |
|
||||
|
||||
- cr3寄存器各个位的意义。cr3是页目录基址寄存器,保存当前页目录的物理地址。
|
||||
|
||||
| 位 | 标签 | 描述 |
|
||||
| ----- | ---- | ---------------- |
|
||||
| | | |
|
||||
| | | |
|
||||
| 12-63 | | PML4的物理基地址 |
|
||||
|
||||
- cr0寄存器控制协处理器和保护控制。
|
||||
|
||||
其它的cr寄存器(cr1, cr5-7, cr9-15)是保留的,访问会产生异常。
|
||||
|
||||
##### model-specific register(MSR)
|
||||
|
||||
###### MTRR寄存器
|
||||
|
||||
MTRR(Memory type range registers)是一组处理器附属功能控制寄存器,它为系统软件提供如何访问内存区域的控制。它使用一组MSRs(model-specific registers)。对内存区域的访问模式有uncached, write-through, write-combining, write-protect,和 write-back.在write-back模式下,先写到cache并将cache标记为dirty,这样cache的内容就会被写到内存。
|
||||
|
||||
- MTRRCAP是一个只读的MSR,编号0xfe,它的作用是获取其它MTRR的信息。其结构如下:
|
||||
|
||||
| 位 | 名称 | 意义 |
|
||||
| ----- | ---- | ------------------------------------------------------ |
|
||||
| 0 - 7 | VCNT | 可变范围寄存器的数量 |
|
||||
| 8 | FIX | 1:支持固定范围寄存器,0:不支持固定范围寄存器 |
|
||||
| 10 | WC | 1:支持write-combining内存,0:不支持write-combining内存 |
|
||||
| 其它 | 保留 | |
|
||||
|
||||
- DEF_TYPE,编号0x2ff
|
||||
|
||||
- Variable-range Register Pair,base和mask两个寄存器成对出现,编号从0x200开始。一共有8对这样的寄存器,可映射8个可变的内存区域。
|
||||
|
||||
- Fixed-range,由11个64位的寄存器组成,共映射了88个固定内存范围。
|
||||
|
||||
- APIC-BASE,编号0x1b。
|
||||
|
||||
###### IA32_EFER
|
||||
|
||||
Extended Feature Enable Register (EFER),使能SYSCALL和SYSRET指令,用于长模式(long mode)的进入和返回。编号0xC0000080。
|
||||
|
||||
| 位 | 标签 | 描述 |
|
||||
| ----- | ----- | ------------------------------ |
|
||||
| 0 | SCE | 系统调用扩展 |
|
||||
| 1-7 | 0 | 保留 |
|
||||
| 8 | LME | 长模式使能 |
|
||||
| 10 | LMA | 长模式active |
|
||||
| 11 | NXE | 非执行使能 |
|
||||
| 12 | SVME | Secure Virtual Machine Enable |
|
||||
| 13 | LMSLE | Long Mode Segment Limit Enable |
|
||||
| 14 | FFXSR | Fast FXSAVE/FXRSTOR |
|
||||
| 15 | TCE | Translation Cache Extension |
|
||||
| 16-63 | 0 | 保留 |
|
||||
|
||||
###### FS.base, GS.base
|
||||
|
||||
FS的编号0xC0000100,GS的编号0xC0000101。
|
||||
|
||||
###### KernelGSBase
|
||||
|
||||
编号0xC0000102。
|
||||
|
||||
###### IA32_TSC_AUX
|
||||
|
||||
编号0xC0000103。是一个辅助的TSC寄存器,与IA32_TSC结合使用。
|
||||
|
||||
##### 调试寄存器
|
||||
|
||||
`DR0-DR3, DR6, DR7`
|
||||
|
||||
##### 测试寄存器
|
||||
|
||||
`TR3-TR5, TR6, TR7`
|
||||
|
||||
##### 保护模式寄存器
|
||||
|
||||
`GDTR, LDTR, TR, IDTR`
|
||||
|
||||
#### cpuid指令
|
||||
|
||||
可以从[osdev](https://wiki.osdev.org/CPUID)获取关于cpuid的详细信息。
|
||||
|
||||
用于获取处理器信息,其操作码为0x0fa2。从eax寄存器获得输入,将信息输出到从eax到edx的四个寄存器。
|
||||
|
||||
| eax(写入) | eax | ebx | ecx | edx |
|
||||
| ---------- | -------------------- | -------------- | -------------- | -------------- |
|
||||
| 0 | 最大的基本功能号 | 厂商信息 | 厂商信息 | 厂商信息 |
|
||||
| 1 | 处理器签名 | Misc Info | 属性标志 | 属性标志 |
|
||||
| 2 | cache和TLB | cache和TLB | cache和TLB | cache和TLB |
|
||||
| 3 | 保留 | 保留 | 处理器序列号 | 处理器序列号 |
|
||||
| 0x80000000 | 最大的扩展功能号 | | | |
|
||||
| 0x80000001 | | | 扩展功能标志 | 扩展功能标志 |
|
||||
| 0x80000002 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 |
|
||||
| 0x80000003 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 |
|
||||
| 0x80000004 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 |
|
||||
| 0x80000005 | | | | |
|
||||
| 0x80000006 | | | L2cache信息 | |
|
||||
| 0x80000007 | | | | 电源管理 |
|
||||
| 0x80000008 | 物理和虚拟地址的位数 | | | |
|
||||
|
||||
当向eax写入1,并执行cpuid指令,可以从eax中得到处理器签名,从edx和ecx得到的是cpu支持哪些功能。edx的第12位MTRR即Memory Type Range Registers。edx的第9位即APIC。
|
||||
|
||||
当向eax写入0x80000001,并执行cpuid指令,可以从edx和ecx得到扩展功能标志。edx的第29位即long mode。
|
||||
|
||||
#### rdmsr和wrmsr指令
|
||||
|
||||
rdmsr指令的作用是读取msr寄存器的内容。msr寄存器由ecx寄存器的值决定,读取的内容高32位放在edx寄存器中,低32位放在eax寄存器中。
|
||||
|
||||
wrmsr指令是把edx:eax的值写入到由ecx指定的msr寄存器中。
|
|
@ -0,0 +1,46 @@
|
|||
参考资料
|
||||
|
||||
https://sourceware.org/binutils/docs/as/CFI-directives.html
|
||||
|
||||
https://sourceware.org/binutils/docs/as/RISC_002dV_002dDirectives.html
|
||||
|
||||
#### 汇编指示(伪操作)
|
||||
|
||||
汇编程序中以"."开头的名称不是指令的助记符,不会被翻译成机器指令,而是给汇编器一些特殊的指示,称为汇编指示(Assembler Directive)或伪操作(Pseudo-operation)。
|
||||
|
||||
##### .section指示
|
||||
|
||||
.section指示把代码划分成若干个段(section),程序被操作系统加载时,每个段被加载到不同的地址,具有不同的读写执行权限。
|
||||
|
||||
- .data段保存程序的数据是可读写的,C程序的全局变量也属于.data段。
|
||||
|
||||
- .text段保存代码,是只读和可执行的。
|
||||
- .bss段是未初始化的数据段
|
||||
|
||||
##### .globl指示
|
||||
|
||||
.globl指示告诉汇编器_start这个符号要被链接器用到,所以要在目标文件的符号表中给它特殊标记。
|
||||
|
||||
##### .long指示
|
||||
|
||||
.long指示声明一组数,每个数32位,相当于C数组。
|
||||
|
||||
##### .byte指示
|
||||
|
||||
.byte指示也是声明一组数,每个数8位
|
||||
|
||||
##### .ascii指示
|
||||
|
||||
声明一个字符串,每个字符取值为相应的ASCII码。和C语言不同的是这样声明的字符串末尾是没有'\0'字符的。
|
||||
|
||||
##### .cfi_startproc和.cfi_closeproc
|
||||
|
||||
它们成对出现,分别在函数的开头和结尾。.cfi_startproc初始化一些内部数据结构,并注入初始CFI指令。.cfi_closeproc关闭由.cfi_startproc打开的入口。
|
||||
|
||||
##### .cfi_undefined
|
||||
|
||||
指定寄存器的值将不能再恢复了。
|
||||
|
||||
##### .option
|
||||
|
||||
修改与汇编代码相关的汇编器选项。
|
|
@ -0,0 +1,23 @@
|
|||
[Home](../)->C语言
|
||||
参考:[GCC用法](https://gcc.gnu.org/onlinedocs/gcc/)
|
||||
《C程序设计语言》
|
||||
|
||||
---
|
||||
[预处理指令](preprocessor)
|
||||
[内联汇编](inline-assembly)
|
||||
|
||||
### 数据类型
|
||||
[struct](struct)
|
||||
|
||||
### 关键字
|
||||
[keyword](keyword)
|
||||
|
||||
### 函数库
|
||||
[printf](printf)
|
||||
[文件操作](file-operation)
|
||||
[字符串操作](string-operation)
|
||||
[builtin函数](builtin-function)
|
||||
[poll](poll)
|
||||
|
||||
### 编译错误汇总
|
||||
[编译错误](compiling-errors)
|
|
@ -0,0 +1,7 @@
|
|||
可以从[gcc onlinedocs](https://gcc.gnu.org/onlinedocs/gcc-4.4.5/gcc/Other-Builtins.html#Other-Builtins)获取更多信息。
|
||||
```c
|
||||
int __builtin_constant_p (exp); // 用来确定exp是否在编译时是常量,是则返回1,否则返回0.
|
||||
long __builtin_expect(long exp,long c); // 为编译器提供分支预测的信息,意为期望exp的值与c的值相等。也可以使用参数'-fprofile-arcs'来让gcc收集分支走向的信息,然而对于很多程序来说这样的信息是很难收集的。exp为一个整型表达式,c必须是一个编译期的常量,返回值为第一个参数exp的值。
|
||||
void __builtin_trap(void); // 此函数将使程序异常的退出。GCC通过宿主机的相关机制或调用abort实现此函数。
|
||||
```
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
官方文档: [Inline-Assembly-HOWTO](http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html) & [Using-Assembly-Language-with-C](https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C)
|
||||
|
||||
#### 基本内联汇编的格式:
|
||||
```c
|
||||
_asm_ _volatile_("instruction list");
|
||||
```
|
||||
#### 带有C/C++表达式的内联汇编格式:
|
||||
```c
|
||||
_asm_ _volatile_("instruction list":output:input:modify);
|
||||
// %0,%1代表占位符,从%0到%9共有10个,分别代表第0个到第9个C表达式。
|
||||
// 匹配限制符从"0"到"9"共10个,也代表的是从0个到9个的C表达式,用于先读后写处理。
|
||||
|
||||
// r: auto select a,b,c,d
|
||||
// q: auto select a,b,c,d
|
||||
// m: memory
|
||||
// g: r or m
|
||||
// a: %eax or %ax or %al
|
||||
// b: %ebx or %bx or %bl
|
||||
// c: %ecx or %cx or %cl
|
||||
// d: %edx or %dx or %dl
|
||||
// D: %edi or %di
|
||||
// S: %esi or %si
|
||||
// i: 立即数
|
||||
```
|
||||
|
||||
#### 一些例子
|
||||
```c
|
||||
/*
|
||||
* 第1个例子
|
||||
*/
|
||||
asm volatile ("inb %1,%0" : "=a" (data) : "d" (port) : "memory");
|
||||
// data是要输出的c表达式,a是要赋值给data的寄存器,=表示data是只写的(什么都没有表示data是只读的,+表示data是可读写的)
|
||||
// C表达式port通过寄存器d输入到当前内联汇编语句中
|
||||
// "=a"(data)为第一个C表达式,其指定的寄存器为a,故%0代表了寄存器a
|
||||
// "d"(port)为第二个C表达式,其指定寄存器为d,故%1代表了寄存器d
|
||||
// 内存被修改了,但在输出表达式中未指明"m"约束,故需在modify部分指明"memory"被修改了。
|
||||
// 如果寄存器在指令列表中被修改了,而输入/输出部分未进行约束,需在modify部分指明这一点。
|
||||
|
||||
/*
|
||||
* 第2个例子
|
||||
*/
|
||||
asm volatile ("outb %0,%1" :: "a" (data), "d" (port));
|
||||
// 本例中没有输出
|
||||
// 将data的值通过寄存器a输入,将port的值通过寄存器d输入
|
||||
// %0代表第一个寄存器a,%1代表第二个寄存器d
|
||||
// 不存在未被约束过的寄存器或内存,故modify部分无须指定
|
||||
|
||||
/*
|
||||
* 第3个例子
|
||||
*/
|
||||
asm volatile (
|
||||
"cld;"
|
||||
"repne; insl;"
|
||||
: "=D" (addr), "=c" (cnt) //通过D寄存器输出给addr,通过c寄存器输出给cnt
|
||||
: "d" (port), "0" (addr), "1" (cnt) //port通过d寄存器输入,addr通过D寄存器输入,cnt通过c寄存器输入
|
||||
: "memory", "cc"); //cc表示内联汇编语句会影响到eflags寄存器
|
||||
|
||||
/*
|
||||
* 第4个例子
|
||||
*/
|
||||
asm volatile ("btsl %1, %0" :"=m" (* (volatile long * )addr) : "Ir" (nr));
|
||||
//将 (* addr) 的第 nr 位设为 1。第一个占位符 %0 与 C 语言变量 addr 对应,第二个占位符 %1 与 C 语言变量 nr 对应。
|
||||
//因此上面的汇编语句代码与下面的伪代码等价:btsl nr, ADDR
|
||||
//该指令的两个操作数不能全是内存变量,因此将nr的限定字符串指定为“Ir”,将nr与立即数或者寄存器相关联,这样两个操作数中只有addr为内存变量。
|
||||
|
||||
/*
|
||||
* 第5个例子
|
||||
*/
|
||||
asm volatile (
|
||||
"int %1;"
|
||||
: "=a" (ret)
|
||||
: "i" (T_SYSCALL), "0" (SYS_exec), "d" (name), "c" (len), "b" (binary), "D" (size)
|
||||
: "memory");
|
||||
// 执行的是 int T_SYSCALL, 执行前先将SYS_exec传给eax,执行完后将eax的值返回给ret。这实际上是系统调用的指令。
|
||||
```
|
|
@ -0,0 +1,44 @@
|
|||
描述c语言中的关键字
|
||||
|
||||
#### attribute
|
||||
|
||||
官方文档:https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
|
||||
|
||||
* align属性:内存内按字节对齐
|
||||
* always_inline属性:GCC会根据代码逻辑判断是否使用inline代码,但如果使用了always_inline,则一定会将函数内嵌进去。
|
||||
* constructor:在main函数运行之前就可运行完毕。
|
||||
* destructor:在进程exit之前执行。
|
||||
* fastcall & regparm:函数传递参数通常是用堆栈,为了加快代码运行速度,使用这两个属性,会用寄存器来传递参数。
|
||||
* packed属性:按实际大小来计算struct,union,enum数据结构。
|
||||
* section属性:把代码放到特定的section中。
|
||||
|
||||
#### extern
|
||||
|
||||
参考自:《The C programming language》1.10外部变量与作用域
|
||||
|
||||
作用: 声明外部变量
|
||||
|
||||
#### inline
|
||||
|
||||
官方文档:https://gcc.gnu.org/onlinedocs/gcc/Inline.html#Inline
|
||||
|
||||
作用:表示此函数被调用时将直接插入,而不是像普通函数那样生成调用代码。
|
||||
|
||||
#### static
|
||||
|
||||
* static全局变量:
|
||||
和其他全局变量一样,都是存储在.data段(已初始化)或者.bss段(未初始化),但它只在定义它的源文件内有效,其他源文件无法访问它。
|
||||
* static局部变量:
|
||||
static局部变量分配在.data段中(普通局部变量分配在栈空间中);只能在其作用域中被访问,而不能被其他函数或源文件访问;其值如果没有被用户初始化,则会被系统初始化为0.
|
||||
* static函数:
|
||||
相当于面向对象语言中的private函数,只在定义他的源文件内有效,其他源文件无法访问它。
|
||||
|
||||
#### volatile
|
||||
|
||||
参考:[volatile对象](https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html#Volatiles)
|
||||
描述:volatile的意思是易变的,此关键字的意思是告诉编译器不要对其所修饰的变量进行优化。
|
||||
|
||||
#### continue
|
||||
|
||||
* 用于循环体中,continue语句会跳过循环体后续的代码,再次进入循环体继续执行。
|
||||
* 而break语句会直接跳出循环体,不再执行循环体。另外,在条件判断时,break语句也用于跳出条件判断的语句块。
|
|
@ -0,0 +1 @@
|
|||
为程序增加诊断功能。
|
|
@ -0,0 +1 @@
|
|||
ctype.h声明了一些测试字符的函数。
|
|
@ -0,0 +1,47 @@
|
|||
getopt.h是用来获取函数参数的。函数getopt只支持短格式,函数getopt_long长、短格式都支持,函数getopt_long_only与getopt_long基本相同,区别在于长选项字串是用"-"开始的,而不是"--"。
|
||||
|
||||
#### 变量
|
||||
|
||||
```
|
||||
extern char *optarg; # 用于getopt和调用者之间通信,当getopt找到带有参数的选项时,参数值放在这里。
|
||||
extern int optind, opterr, optopt;
|
||||
# optind:argv里下一个待处理参数的索引。用于对getopt的重复调用。
|
||||
# opterr:调用者在这里存储0以禁止getopt因未识别的选项而打印错误消息。
|
||||
# optopt:设置未识别的选项字符。
|
||||
```
|
||||
|
||||
#### getopt
|
||||
|
||||
`int getopt(int argc, char * const argv[], const char *optstring);`
|
||||
|
||||
作用:解析命令行参数。当重复调用,它会依次返回各个参数里的字符串。当无参数可解析时,返回-1。
|
||||
|
||||
argc:参数数量,来自于main函数。
|
||||
|
||||
argv:参数数组,来自于main函数。以'-'开头的被认为是一个参数。
|
||||
|
||||
optstring:
|
||||
|
||||
#### getopt_long
|
||||
|
||||
```
|
||||
extern int getopt_long (int argc, char *const argv[],
|
||||
const char *optstring,
|
||||
const struct option *longopts, int *longindex);
|
||||
```
|
||||
|
||||
#### getopt_long_only
|
||||
|
||||
```
|
||||
extern int getopt_long_only (int argc, char *const argv[],
|
||||
const char *optstring,
|
||||
const struct option *longopts, int *longindex);
|
||||
```
|
||||
|
||||
argc, argv:直接从main函数传递而来
|
||||
|
||||
shortopts:短选项字符串。需要传递参数时要在后面加冒号。
|
||||
|
||||
longopts:结构体的数组,用于存放长选项参数。
|
||||
|
||||
longind:长选项在longopts中的索引,用于调试,一般为NULL。
|
|
@ -0,0 +1 @@
|
|||
math.h中声明了一些数学函数和宏。
|
|
@ -0,0 +1,4 @@
|
|||
#### 作用:
|
||||
- 等待一个文件描述符上的事件。和系统调用select()的作用相近,它会等待一个文件描述符的集合是否准备好进行I/O。
|
||||
#### 描述:
|
||||
- 函数原型:`int poll(struct pollfd *fds, nfds_t nfds, int timeout)`
|
|
@ -0,0 +1 @@
|
|||
非局部跳转
|
|
@ -0,0 +1 @@
|
|||
处理异常
|
|
@ -0,0 +1,37 @@
|
|||
### 简介
|
||||
|
||||
关于可变参数的处理,详见`stdarg.h`
|
||||
|
||||
通过`man stdarg`获取帮助
|
||||
|
||||
### 数据结构
|
||||
|
||||
#### va_list
|
||||
|
||||
是一个变量类型,它将依次指向每个参数。
|
||||
|
||||
### 宏
|
||||
|
||||
#### va_start
|
||||
|
||||
`void va_start(va_list ap, last);`
|
||||
|
||||
在访问可变参数前,必须用va_start初始化变量ap。last是可变参数之前最后一个参数的名称。
|
||||
|
||||
#### va_arg
|
||||
|
||||
`type va_arg(va_list ap, type);`
|
||||
|
||||
将返回下一个参数。
|
||||
|
||||
#### va_end
|
||||
|
||||
`void va_end(va_list ap);`
|
||||
|
||||
参数处理完毕后,必须调用va_end一次。
|
||||
|
||||
#### va_copy
|
||||
|
||||
`void va_copy(va_list dest, va_list src);`
|
||||
|
||||
将可变参数表src复制到dest。
|
|
@ -0,0 +1,118 @@
|
|||
#### 基本概念
|
||||
|
||||
##### 流
|
||||
|
||||
流是数据的源或目的地,它实际上对应着的是一个设备,比如磁盘。程序开始执行时,stdin, stdout, stderr这3个流已经打开。
|
||||
|
||||
#### 格式化输出的函数
|
||||
|
||||
```c
|
||||
int fprintf(FILE *stream, const char *format, ...)
|
||||
// fprintf函数将带有格式标记的字符串format写到stream流中。返回值是实际写入的字符数,若出错则返回一个负值。
|
||||
- printf(...)等价于fprintf(stdout, ...)
|
||||
- dprintf是将输出流设定为文件描述符fd。
|
||||
- sprintf(char *s,const char *format, ...)是将format写入到字符串s中。
|
||||
- snprintf类似于sprintf,但给定了字符串的长度。
|
||||
- vprintf等价于printf,但用arg代替了可变参数表,arg由宏va_start或va_arg初始化。
|
||||
- vfprintf等价于fprintf,但用arg代替了可变参数表,arg由宏va_start或va_arg初始化。
|
||||
- vdprintf等价于dprintf,但用arg代替了可变参数表。
|
||||
- vsprintf等价于sprintf,但用arg代替了可变参数表,arg由宏va_start或va_arg初始化。
|
||||
- vsnprintf等价于snprintf,但用arg代替了可变参数表。
|
||||
|
||||
```
|
||||
|
||||
##### format的结构
|
||||
|
||||
格式化字符串format = 普通字符串 + 转换说明
|
||||
转换说明 = % + 约束部分 + 转换字符
|
||||
##### format约束部分
|
||||
|
||||
标志:
|
||||
- // 左对齐
|
||||
+ // 加正负号
|
||||
空格 // 如果第一个字符不是正负号,则在其前面加空格
|
||||
0 // 对于数值转换,当输出长度小于字段宽度时,则在其前面填充0
|
||||
# // 指定另一种输出形式,o转换则加前缀o,x或X转换加前缀0x或0X,e、E、f、g、G转换,结果有小数时才给出小数点。
|
||||
数值(宽度) // 指定最小字段宽度
|
||||
. // 分隔字段宽度和精度
|
||||
数值(精度) //对于字符串指定最大长度,e、E或f转换指定小数点后数位的长度,g或G转换指定有效位长度,对整型数则指定数位长度。
|
||||
h // 按short或unsigned short输出。
|
||||
l // 按long或unsigned long输出。
|
||||
L // 按long double输出。
|
||||
* // 宽度或精度的任何一个都可以用*指定
|
||||
##### format转换字符
|
||||
d,i // int,有符号十进制数
|
||||
u // unsigned int,无符号十进制数
|
||||
o // unsigned int,无符号八进制数
|
||||
x,X // unsigned int,无符号十六进制(无前导0x或0X)
|
||||
c // int
|
||||
s // char *,字符串
|
||||
f // double,双精度型
|
||||
e,E // double,双精度型(指数形式)
|
||||
g,G // double,f或e,E类型,取决于限定条件
|
||||
p // void *,指针
|
||||
n // int *,到当前为止输出的字符数
|
||||
% // 不转换,打印%
|
||||
|
||||
#### 格式化输入函数
|
||||
|
||||
```c
|
||||
int fscanf(FILE *stream, const char *format, ...); // 依据格式串format从*stream中读取输入,把值赋给后续各个参数
|
||||
int scanf(const char *format, ...); // 等价于fscanf(stdin, *format, ...)
|
||||
int sscanf;
|
||||
```
|
||||
|
||||
#### 字符输入输出函数
|
||||
|
||||
```c
|
||||
int fgetc();
|
||||
int fgets();
|
||||
int fputc();
|
||||
int fputs();
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 文件操作函数
|
||||
|
||||
```c
|
||||
FILE *fopen(const char *path, const char *mode);
|
||||
// 打开文件名为path的文件,并关联到一个流。参数mode指打开的模式。
|
||||
FILE *fdopen(int fd, const char *mode);
|
||||
FILE *freopen(const char *path, const char *mode, FILE *stream);
|
||||
int fclose(FILE *stream); // 关闭流
|
||||
```
|
||||
|
||||
##### 解读mode
|
||||
|
||||
- r:以读的方式打开文件,流指向文件开头。
|
||||
- r+:以读写的方式打开,流指向文件开头。
|
||||
- w:将文件截断为0长度,或创建可写的文件,流指向文件开头。
|
||||
- w+:以读写的方式打开,文件不存在则会创建,存在则截断。流指向文件开头。
|
||||
- a:以附加的方式打开,文件不存在则会创建,流指向文件末尾。
|
||||
- a+:以附加且可读的方式打开,文件不存在则会创建,但读是从文件开头,写是从文件末尾。
|
||||
- 还可以附加`b`表示是二进制,但这对POSIX兼容系统来说没有效果,而只是为了与C89兼容。
|
||||
|
||||
#### 直接输入输出函数
|
||||
|
||||
```c
|
||||
size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream); // 从流stream中最多读取nobj个长度为size的对象,保存到ptr指向的数组中。返回读取的对象数,此值可能小于nobj。
|
||||
size_t fwrite(void *ptr, size_t size, size_t nobj, FILE *stream); // 从ptr指向的数组中读取nobj个长度为size的对象,输出到流stream中。返回输出的对象数。
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 文件定位函数
|
||||
|
||||
```c
|
||||
int fseek(FILE *stream, long offset, int origin); // 设置流stream的位置为从origin开始的第offset个字符,后续的读写操作将从新位置开始。origin的值可以是SEEK_SET(文件开始),SEEK_CUR(当前位置),SEEK_END(文件末尾)
|
||||
long ftell(FILE *stream); // 返回stream流当前的位置
|
||||
```
|
||||
|
||||
#### 错误处理函数
|
||||
|
||||
```c
|
||||
void perror(const char *s); // 打印字符串s与错误信息
|
||||
int feof(FILE *stream); // 测试流stream的文件结束指示符,如果设置了则返回非0。
|
||||
```
|
||||
|
|
@ -0,0 +1 @@
|
|||
实用函数
|
|
@ -0,0 +1,37 @@
|
|||
1. 字符串操作
|
||||
* strcpy(p, p1) 复制字符串
|
||||
* strncpy(p, p1, n) 复制指定长度字符串
|
||||
* strcat(p, p1) 附加字符串
|
||||
* strncat(p, p1, n) 附加指定长度字符串
|
||||
* strlen(p) 取字符串长度
|
||||
* strcmp(p, p1) 比较字符串
|
||||
* strcasecmp忽略大小写比较字符串
|
||||
* strncmp(p, p1, n) 比较指定长度字符串
|
||||
* strchr(p, c) 在字符串中查找指定字符
|
||||
* strrchr(p, c) 在字符串中反向查找
|
||||
* strstr(p, p1) 查找字符串
|
||||
* strtok(p,p1) 依据p1中的字符分割字符串p
|
||||
* strpbrk(p, p1) 以目标字符串的所有字符作为集合,在当前字符串查找该集合的任一元素
|
||||
* strspn(p, p1) 以目标字符串的所有字符作为集合,在当前字符串查找不属于该集合的任一元素的偏移
|
||||
* strcspn(p, p1) 以目标字符串的所有字符作为集合,在当前字符串查找属于该集合的任一元素的偏移
|
||||
* 具有指定长度的字符串处理函数在已处理的字符串之后填补零结尾符
|
||||
|
||||
2. 字符串到数值类型的转换
|
||||
* strtod(p, ppend) 从字符串 p 中转换 double 类型数值,并将后续的字符串指针存储到 ppend 指向的 char* 类型存储。
|
||||
* strtol(p, ppend, base) 从字符串 p 中转换 long 类型整型数值,base 显式设置转换的整型进制,设置为 0 以根据特定格式判断所用进制,0x, 0X 前缀以解释为十六进制格式整型,0 前缀以解释为八进制格式整型
|
||||
* atoi(p) 字符串转换到 int 整型
|
||||
* atof(p) 字符串转换到 double 符点数
|
||||
* atol(p) 字符串转换到 long 整型
|
||||
|
||||
3. 字符检查
|
||||
* isalpha() 检查是否为字母字符
|
||||
* isupper() 检查是否为大写字母字符
|
||||
* islower() 检查是否为小写字母字符
|
||||
* isdigit() 检查是否为数字
|
||||
* isxdigit() 检查是否为十六进制数字表示的有效字符
|
||||
* isspace() 检查是否为空格类型字符
|
||||
* iscntrl() 检查是否为控制字符
|
||||
* ispunct() 检查是否为标点符号
|
||||
* isalnum() 检查是否为字母和数字
|
||||
* isprint() 检查是否是可打印字符
|
||||
* isgraph() 检查是否是图形字符,等效于 isalnum() | ispunct()
|
|
@ -0,0 +1,50 @@
|
|||
string.h定义了两组函数,一组以str开头,一组以mem开头
|
||||
|
||||
#### 定位字符串中的字符
|
||||
|
||||
- char *strchr(const char *s, int c);
|
||||
|
||||
返回字符c在字符串s中第一次匹配时的指针,若不能匹配则返回NULL。
|
||||
|
||||
- char *strrchr(const char *s, int c);
|
||||
|
||||
返回字符c在字符串s中最后一次匹配时的指针,若不能匹配则返回NULL。
|
||||
|
||||
- char *strchrnul(const char *s, intc);
|
||||
|
||||
类似于strchr,不同点在于如果没有在s里匹配到c,不是返回的NULL,而是返回在s后面那个null字节(应该指的是字符串后面那个`\0`字节)的指针。
|
||||
|
||||
#### 连接两个字符串
|
||||
|
||||
- char *strcat(char *dest, const char *src);
|
||||
|
||||
在字符串dest后面附加src字符串。要求两个字符串不可重叠,且dest要有足够的空间。
|
||||
|
||||
- char *strncat(char *dest, const char *src, size_t n);
|
||||
|
||||
类似于strcat,不同点在于最多只使用src里的n个字节,如果src包含n个或更多的字符,它也不必使用null作为终结符。
|
||||
|
||||
#### 复制字符串
|
||||
|
||||
```
|
||||
char *strdup(const char *s); //复制字符串s,并返回其指针。
|
||||
char *strndup();
|
||||
char *strdupa();
|
||||
char *strndupa();
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 复制内存区域
|
||||
|
||||
- void *memcpy(void *dest, cosnt void *src, size_t n);
|
||||
|
||||
从src向dest复制n个字节的内容。要求src和dest两个内存区域不可重叠。其返回值是对dest的指针。
|
||||
|
||||
- bcopy
|
||||
- memccpy
|
||||
- memmove
|
||||
- mempcpy
|
||||
- strcpy
|
||||
- strncpy
|
||||
- wmemcpy
|
|
@ -0,0 +1 @@
|
|||
日期与时间
|
|
@ -0,0 +1,19 @@
|
|||
#### 可变参数宏
|
||||
|
||||
```c
|
||||
#define dgbmsg(fmt, ...) printf(fmt, __VA_ARGS__)
|
||||
// ...代表一个可变参数表, 保留名__VA_ARGS__把参数传递给宏
|
||||
// ##的作用是:如果可变参数为空或被忽略,预处理器会去掉它前面的逗号
|
||||
```
|
||||
|
||||
#### 查看GCC预定义的宏
|
||||
|
||||
`gcc -E -dM - </dev/null`
|
||||
|
||||
#### \__attribute__
|
||||
|
||||
\__attribute__关键字用于定义变量或结构体的属性。
|
||||
|
||||
- section子项的用法
|
||||
|
||||
`__attribute__((section("section_name")))`,其作用是将函数或数据放入名为setion_name的段。
|
|
@ -0,0 +1,13 @@
|
|||
**参考自《C程序设计语言》B&D**
|
||||
#### 文件包含
|
||||
用法: `#include "文件名"` 或 `#include <文件名>`
|
||||
#### 宏替换
|
||||
用法: `#define 名字 替换文本`
|
||||
作用: 用 *替换文本* 替换 *名字*
|
||||
用法: `#undef 名字`
|
||||
作用: 取消 *名字* 的宏定义
|
||||
#### 条件包含
|
||||
1. `#if`后包含的整型表达式若非0,则包含其后各行,直到`#endif`,`#elif`或`#else`语句为止。`#elif`的作用类似于`else if`。
|
||||
2. 表达式 `defined(名字)` 的规则是:若名字已定义其值为1,否则其值为0。
|
||||
3. `#ifndef 名字` 的作用等同于 `#if !defined(名字)`
|
||||
4. `#ifdef 名字` 的作用等同于 `#if defined(名字)`
|
|
@ -0,0 +1,2 @@
|
|||
结构体中冒号的作用:
|
||||
* 位域:冒号后面的数字表示冒号前面的变量占的位数
|
|
@ -0,0 +1,145 @@
|
|||
1. 介绍
|
||||
|
||||
Chisel只是一些专门的**类定义**,预定义的**对象**,和对Scala**使用约定**的集合。所以,写Chisel程序实际上就是用Scala语言描述硬件。
|
||||
|
||||
Chisel生成的是可综合的Verilog代码。Chisel也可以生成一个用C++实现的快速的、周期精确的RTL模拟器。
|
||||
|
||||
Verilog和VHDL是作为硬件**模拟**语言而开发的,之后它们才成为硬件**综合**的基础。它们的大部分语义并不适合硬件综合,事实上大部分是不可综合的。而Chisel描述的直接就是可综合的电路,而且它更接近于Verilog这样低层次的硬件描述语言,而不是高层的综合系统。
|
||||
|
||||
2. 硬件表达
|
||||
|
||||
本版本只支持二进制逻辑,不支持三态信号。
|
||||
|
||||
3. 数据类型
|
||||
|
||||
- Bits:比特的原始组合
|
||||
- Sint:有符号整数
|
||||
- Uint:无符号整数
|
||||
- Bool:布尔值
|
||||
- Bundles:带有命名域的值的组合,类似于C语言里的结构体
|
||||
- Vecs:值的可索引的组合
|
||||
|
||||
4. 组合电路
|
||||
|
||||
Chisel里的电路是结点组成的图。每个结点都是**硬件操作符**,由零或多个输入驱动一个输出。
|
||||
|
||||
- &:按位与
|
||||
- |:按位或
|
||||
- ~:按位非
|
||||
|
||||
简单表达式可以直接转换为电路树,在叶子和操作符上形成内部结点。表达式的最终输出是从树根上的操作符提取出来的。
|
||||
|
||||
- val:用来命名值不会再变化的变量
|
||||
|
||||
Chisel也支持**电线**作为结点,可用来赋值或连接到其它结点。
|
||||
|
||||
- Wire:电线
|
||||
|
||||
5. 内置操作符
|
||||
|
||||
- 按位操作符:~, &, `(overflow),^
|
||||
- 按位归约:andR, orR, xorR
|
||||
- 相等比较:===, =/=
|
||||
- 移位:<<, >>
|
||||
- 位域操作:x(), Fill(), Cat()
|
||||
- 逻辑运算:! && ` Mux()
|
||||
- 算术运算:+ +% +& - -% -& * / %
|
||||
- 算术比较:> >= < <=
|
||||
|
||||
6. 功能抽象
|
||||
|
||||
我们可以定义函数来提取出重复的逻辑,在一个设计里重复使用。
|
||||
|
||||
- 定义函数:def
|
||||
|
||||
7. Bundles和Vecs
|
||||
|
||||
Bundles和Vecs是使用户可以扩展数据类型的类。Bundle相当于C语言里的结构体,Vec相当于数组。
|
||||
|
||||
8. 端口
|
||||
|
||||
端口是硬件组件之间的接口。基本端口Input和Output可以合成更复杂的端口。
|
||||
|
||||
9. 模块
|
||||
|
||||
Chisel的模块非常类似于在生成的电路中定义层次结构的Verilog模块。
|
||||
|
||||
用户定义的模块被定义为如下的类:
|
||||
|
||||
- 继承自Module
|
||||
- 包含封装在模块IO方法的接口
|
||||
- 存储在一个名为io的端口字段中
|
||||
- 在其构造函数中连接子电路
|
||||
|
||||
10. 黑箱
|
||||
|
||||
Chisel的黑盒用来表示外部定义的模块。
|
||||
|
||||
11. 状态元素
|
||||
|
||||
最简单的状态元素是正边沿触发寄存器,可表示如下:
|
||||
|
||||
`val reg = RgeNext(in)`
|
||||
|
||||
此电路的输出是延迟一个时钟周期后输入信号in的复制。
|
||||
|
||||
计数器是重要的时序电路。
|
||||
|
||||
12. 内存
|
||||
|
||||
- ROM
|
||||
|
||||
用户可通过Vec定义只读内存
|
||||
|
||||
- MEM
|
||||
|
||||
- Masks
|
||||
|
||||
13. 接口与bulk连接
|
||||
|
||||
- 端口:子类与嵌套
|
||||
- Bundle Vectors
|
||||
- Bulk Connections
|
||||
|
||||
14. 功能模块创建
|
||||
|
||||
15. Mux与输入选择
|
||||
|
||||
- Mux:2-输入选择器
|
||||
- MuxCase:多路选择器
|
||||
- MuxLookup:可索引多路复用器
|
||||
- Mux1H:接收一系列选择器和值,返回与被设置的选择器相关联的值。
|
||||
|
||||
16. 多态与参数化
|
||||
|
||||
此部分是高级的,可在第一次阅读时跳过。
|
||||
|
||||
17. 多时钟域
|
||||
|
||||
18. 注解:扩展Chisel和Firrtl
|
||||
|
||||
使用import引用我们需要的组件。
|
||||
|
||||
Write a transform
|
||||
|
||||
创建一个注解工厂
|
||||
|
||||
创建一个注解器
|
||||
|
||||
使用注解器
|
||||
|
||||
19. 运行时发生了什么?
|
||||
|
||||
执行模式
|
||||
|
||||
运行用scala写的chisel代码
|
||||
|
||||
执行模式的层次
|
||||
|
||||
执行模式代码
|
||||
|
||||
所有的选项
|
||||
|
||||
20. Chisel3 vs Chisel2
|
||||
|
||||
21. 致谢
|
|
@ -0,0 +1,115 @@
|
|||
#### 简介
|
||||
|
||||
Rocket是一个开源的System-on-Chip设计生成器,用以生成可综合的RTL (所以Rocket不是单个的设计实例,而是一个设计实例的生成器,可以生成多种设计实例)。
|
||||
|
||||
Rocket是用的Chisel语言写的,一种嵌入在Scala语言里的开源硬件构建语言。它构建了一个RISC-V的平台。它是由一些参数化的构建库组成的,就是用这些构建库来生成各种各样的RTL实例的。
|
||||
|
||||
Rocket生成通用处理器的核,它以RISC-V为ISA,其中**Rocket**生成有序的核,**BOOM**生成无序的核。
|
||||
|
||||
#### 仓库内容
|
||||
|
||||
##### 子模块
|
||||
|
||||
- [chisel3](https://github.com/ucb-bar/chisel3):用来生成RTL(Register-Transfer Level)
|
||||
- [firrtl](https://github.com/ucb-bar/firrtl):(Flexible Internal Representation for RTL),是RTL的中间表示,由Chisel3来使用,通过它来生成最终代码(Verilog, C等),详细介绍见[specification][6]
|
||||
- [hardfloat](https://github.com/ucb-bar/berkeley-hardfloat):浮点单元的Chisel代码,用于融合的乘加运算、整数浮点数转换、不同精度的浮点数转换
|
||||
- [riscv-tools](https://github.com/riscv/riscv-tools):riscv软件生态环境,与本仓库中的RTL一起工作
|
||||
- [torture](https://github.com/ucb-bar/riscv-torture):生成和执行随机指令流,用于对本设计进行压力测试
|
||||
|
||||
##### scala包
|
||||
|
||||
在`src/main/scala`目录下
|
||||
|
||||
- amba:这个RTL包是AMBA协议的总线实现,包括AXI4, AHB-lite, APB
|
||||
- config:通过动态范围的参数化库来配置生成器
|
||||
- coreplex:将其它包的各种组件粘合在一起,生成一个完整的coreplex
|
||||
- devices:外围设备的实现,包括调试模块和各种TL从设备。
|
||||
- diplomacy:通过两阶段的硬件细化来扩展chisel,两个阶段的交互详情见[paper][7]
|
||||
- groundtest:生成可综合的硬件测试器,通过发出随机内存访问来对非核内存进行压力测试
|
||||
- jtag:生成JTAG总线接口的定义
|
||||
- regmapper:生成带有标准化接口的从设备,用于访问其内存映射的寄存器
|
||||
- rocket:生成rocket核和L1缓存
|
||||
- tile:包含能与核心相结合来构建tiles的组件,比如FPU和加速器。
|
||||
- tilelink:用于生成Tilelink协议的总线实现,还包含了各种适配器和协议转换器。
|
||||
- system:用Chisel详述coreplex的特定配置
|
||||
- unittest:用于生成单个模块的硬件测试器
|
||||
- util:可供其它包重用的组件
|
||||
|
||||
##### 其它资源
|
||||
|
||||
- bootrom:BootROM中的first-stage bootloader源代码
|
||||
- csrc:用于Verilator模拟的C源代码
|
||||
- emulator:编译和运行Verilator
|
||||
- project:由sbt使用,用于scala编译和构建
|
||||
- regression:持续集成和nightly regression套件
|
||||
- scripts:解析模拟输出或操作源文件
|
||||
- vsim:编译和运行Synopsys VCS
|
||||
- vsrc:verilog源文件,包含接口、harnesses和VPI
|
||||
|
||||
##### 扩展Top-Level设计
|
||||
|
||||
详见[project-template](https://github.com/ucb-bar/project-template)
|
||||
|
||||
#### 使用FPGA
|
||||
|
||||
##### 编译rocket
|
||||
|
||||
- 约定:**ROCKET_DIR**为rocket-chip所在的目录
|
||||
- 约定:**RISCV_DIR**为riscv所安装的目录
|
||||
|
||||
```shell
|
||||
# 1. 下载rocket源码
|
||||
git clone https://github.com/ucb-bar/rocket-chip.git
|
||||
cd rocket-chip
|
||||
git submodule update --init
|
||||
# 2. 编译riscv环境(因为对应了特定的提交版本,所以即使有编译好的环境也需要重新编译)
|
||||
export RISCV=$RISCV_DIR
|
||||
cd $ROCKET_DIR/riscv-tools
|
||||
git submodule update --init --recursive
|
||||
export MAKEFLAGS="$MAKEFLAGS -j8"
|
||||
./build.sh
|
||||
# 3. 编译生成verilog(生成的代码在$ROCKET_DIR/vsim/generated-src)
|
||||
cd $ROCKET_DIR/vsim
|
||||
sudo apt install openjdk-8-jdk git device-tree-compiler # need to install first
|
||||
make verilog CONFIG=DefaultFPGAConfig
|
||||
# 在使用以上命令时会出现大量unable to get resource的错误提示
|
||||
# this is a bug with ubuntu 18.04, when I use 16.04, it resolved.
|
||||
# 4. 保持仓库更新
|
||||
cd $ROCKET_DIR
|
||||
git pull origin master
|
||||
git submodule update --init --recursive
|
||||
# 如果riscv-tools的版本改变了,需要重新编译
|
||||
cd riscv-tools
|
||||
./build.sh
|
||||
```
|
||||
|
||||
##### 使用FPGA
|
||||
|
||||
```
|
||||
git clone https://github.com/ucb-bar/fpga-zynq
|
||||
git cd fpga-zynq
|
||||
cd zedboard
|
||||
make init-submodules # 初始化子模块
|
||||
make rocket # 生成新代码
|
||||
make project # 建立vivado项目
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### 使用xxx
|
||||
|
||||
##### 使用VLSI工具
|
||||
|
||||
#### 参数化
|
||||
|
||||
#### 使用GDB调试
|
||||
|
||||
#### 参考资源
|
||||
|
||||
[1]:https://github.com/ucb-bar/fpga-zynq "在zynq FPGA开发板上跑RISC-V所需要的文件"
|
||||
[2]:https://github.com/freechipsproject/rocket-chip "rocket源码"
|
||||
[3]:https://www2.eecs.berkeley.edu/Pubs/TechRpts/2016/EECS-2016-17.pdf "rocket的详细说明文档"
|
||||
[4]:http://www.lowrisc.org/docs/tagged-memory-v0.1/rocket-core/ "rocket core overview"
|
||||
[5]:http://www.lowrisc.org/docs/tagged-memory-v0.1/rocket-chip/ "rocket chip overview"
|
||||
[6]:https://www2.eecs.berkeley.edu/Pubs/TechRpts/2016/EECS-2016-9.pdf "Specification for the FIRRTL Language"
|
||||
[7]:https://carrv.github.io/2017/papers/cook-diplomacy-carrv2017.pdf "cook-diplomacy-carrv2017"
|
|
@ -0,0 +1,27 @@
|
|||
#### 简介
|
||||
|
||||
sodor实现了若干riscv核,它们都实现了RV32b的2.0版本,都不支持虚拟内存,只实现了m-mode的priv 1.10版本。它们使用的verilog文件是用chisel3生成的,它与C++一起生成和运行Sodor仿真器。
|
||||
|
||||
| 流水 | | |
|
||||
| ---- | -------------------------------- | ---- |
|
||||
| 1级 | ISA模拟器 | |
|
||||
| 2级 | 演示流水 | |
|
||||
| 3级 | 使用连续内存 | |
|
||||
| 5级 | 可以在完全绕过和完全互锁之间切换 | |
|
||||
| 5级 | 基于micro-coded实现 | |
|
||||
|
||||
#### 运行
|
||||
|
||||
##### 编译模拟器
|
||||
|
||||
```
|
||||
git clone https://github.com/ucb-bar/riscv-sodor.git
|
||||
cd riscv-sodor
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 参考资料
|
||||
|
||||
[Learning journey](https://github.com/librecores/riscv-sodor/wiki/Learning-journey)
|
|
@ -0,0 +1,13 @@
|
|||
来源:[w3school](http://www.w3school.com.cn)
|
||||
* meta标签:提供页面元信息(必须出现在head内部,必须以值/标签的形式成对传递)
|
||||
content:定义与 http-equiv 或 name 属性相关的元信息
|
||||
http-equiv:把content属性关联到http头部
|
||||
name:把content属性关联到一个名称
|
||||
scheme:定义翻译content属性值的格式
|
||||
* link标签:连接一个样式表(只能出现在head内部)
|
||||
href:规定被链接文档的位置
|
||||
rel:规定前文档与被链接文档的关系
|
||||
* div标签:把文档分区或分节
|
||||
* ul标签:定义无序列表
|
||||
* li标签:定义列表项目
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#### Thread
|
||||
|
||||
程序执行时的线程。Java虚拟机允许一个程序有多个线程。
|
||||
|
||||
##### 方法
|
||||
|
||||
- `static void sleep(long millis)`
|
||||
|
||||
让当前正在执行的线程休眠指定的毫秒数。
|
|
@ -0,0 +1,13 @@
|
|||
描述:包含collections框架,legacy collection类,事件模型,日期与时间工具,国际化和各种实用的类(a string tokenizer, 一个随机数生成器和一个位数组)
|
||||
Interfaces:
|
||||
Classes:
|
||||
StringTokenizer(这个类充许应用程序将字符串拆分成tokens)
|
||||
继承:java.lang.Object
|
||||
构造方法:
|
||||
StringTokenizer(String str) //为str构造一个字符串标记器
|
||||
方法:
|
||||
boolean hasMoreTokens() //测试此标记器里的字符串是否有更多的tokens
|
||||
nextToken() //从此字符串标记器里返回下一个token
|
||||
Enums:
|
||||
Exceptions:
|
||||
Errors:
|
|
@ -0,0 +1,23 @@
|
|||
#### 接口与抽象类的关系
|
||||
|
||||
1. 类之间的共同行为,即可以用**父类与子类**的方式来表示,也可以用**接口**来表示。
|
||||
2. 接口与抽象类很相似,但它的目的是指明类的共同行为,这些类可能是相关的,也可能是不相关的。
|
||||
3. 类与类之间的继承关系是**扩展**(extends),而类与接口之间的继承关系是**实现**(implements)。
|
||||
4. 一个类只能继承**一个**父类,但可以实现**多个**接口。
|
||||
5. 一个接口可以继承多个接口。
|
||||
6. 抽象类的属性是**无限制的**,接口的属性必须是**public static final**。
|
||||
7. 抽象类**有**构造方法,接口**没有**构造方法。
|
||||
8. 抽象类对方法**无限制**,接口的所有方法必须是**公共的抽象实例方法**。
|
||||
|
||||
#### 抽象类
|
||||
|
||||
1. 在继承的层次结构中,父类总是越来越抽象,子类总是越来越具体。当父类设计的非常抽象以至于没有任何的实例的时候,就成为了**抽象类**。
|
||||
2. 当父类的方法依赖于子类的具体属性,而无法在父类中实现时,这样的方法就叫**抽象方法**。
|
||||
3. 为什么要有抽象方法呢?因为这样可以方便两个属性不同的子类之间进行运算。
|
||||
4. 抽象类包含抽象方法,不可用于创建对象。这些抽象方法将在抽象类的子类中实现。抽象类和抽象方法用**abstract**关键字修饰。注意:抽象类也可以**不包含**抽象方法,抽象类的父类也可能是具体的。
|
||||
5. 抽象类的**构造方法**为protected,因为它只被子类使用。
|
||||
|
||||
####接口
|
||||
|
||||
1. **接口**是一种与**类**相似的结构,只包含常量和抽象方法。
|
||||
2. 接口使用**interface**关键字修饰。
|
|
@ -0,0 +1,23 @@
|
|||
### 来源:
|
||||
* [知乎](https://www.zhihu.com/question/25483589)
|
||||
* [阮一峰_asm.js和Emscripten入门教程](http://www.ruanyifeng.com/blog/2017/09/asmjs_emscripten.html)
|
||||
* 《JavaScript高级程序设计》
|
||||
|
||||
### 工具
|
||||
* asm.js
|
||||
是由Mozilla提出的一个基于JS的语法标准,主要是为了解决JS引擎的执行效率问题
|
||||
* Emscripten
|
||||
是Mozilla的一个实验性项目,目的是把C/C++开发的应用编译成JS或HTML5的应用
|
||||
emcc是Emscripten的编译器前端
|
||||
**注意:** 在ubuntu系统通过apt命令安装存在问题,应选择从源码编译安装
|
||||
* uglifyjs是用JavaScript写的压缩JavaScript代码的工具
|
||||
**格式**:`uglifyjs [选项] [文件名]`
|
||||
**--compress**:启用压缩选项
|
||||
**--mangle**:输出变量名替换后的文件
|
||||
|
||||
### JavaScript语法
|
||||
* JavaScript = ECMAscript + DOM + BOM
|
||||
* 数据类型 (5种基本数据类型和2种复杂数据类型)
|
||||
1. 基本数据类型:Undefined, Boolean, Number, String, Null
|
||||
2. 复杂数据类型:Object, Array
|
||||
3. 变量:字母、数字、$、下划线的组合
|
|
@ -0,0 +1,36 @@
|
|||
1. `sudo apt update //更新软件包到最新状态`
|
||||
|
||||
2. `sudo apt install python2.7 //安装python2.7`
|
||||
|
||||
3. `sudo apt install nodejs //安装nodejs`
|
||||
|
||||
4. `sudo apt install build-essential 安装gcc`
|
||||
`sudo apt install cmake //安装cmake`
|
||||
|
||||
5. `sudo apt install git-core //安装git-core`
|
||||
|
||||
6. `sudo apt install default-jre //安装JavaScript`
|
||||
|
||||
7. 编译安装Fastcomp(LLVM + Clang)
|
||||
|
||||
```
|
||||
mkdir myfastcomp
|
||||
cd myfastcomp
|
||||
git clone https://github.com/kripken/emscripten-fastcomp
|
||||
cd emscripten-fastcomp
|
||||
git clone https://github.com/kripken/emscripten-fastcomp-clang tools/clang
|
||||
mkdir duild
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86;JSBackend" -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DCLANG_INCLUDE_EXAMPLES=OFF -DCLANG_INCLUDE_TESTS=OFF // 使用cmake配置(cmake或configure二选一)
|
||||
../configure --enable-optimized --disable-assertions --enable-targets=host,js // 使用configure配置(cmake或configure二选一)
|
||||
cat /proc/cpuinfo | grep "^cpu cores" | uniq // 查询CPU核心数量,充分利用多核可以加快编译速度
|
||||
make -j2 // 上步查到是2核,所以我用-j2参数,慢慢等待吧。。。
|
||||
```
|
||||
|
||||
8. `git clone https://github.com/kripken/emscripten.git //复制Emscripten`
|
||||
|
||||
9. 修改配置文件~/.emscripten,将LLVM_ROOT设置为第7步的目录`.../myfastcomp/emscripten-fastcomp/build/bin`
|
||||
|
||||
10. `./emcc -v //在emscripten目录运行此命令,如果没报错即为安装正确`
|
||||
|
||||
11. `./emcc tests/hello_world.c //如果没报错即为运行正确`
|
|
@ -0,0 +1,65 @@
|
|||
#### 特殊目录:
|
||||
|
||||
. //当前目录
|
||||
.. //上一层目录
|
||||
- //上一个目录
|
||||
~ //当前用户的用户目录
|
||||
~a //用户a的用户目录
|
||||
|
||||
#### 目录操作:
|
||||
|
||||
cd // change directory
|
||||
pwd // print working directory
|
||||
mkdir // make directory
|
||||
rmdir // remove directory
|
||||
|
||||
#### 文件与目录管理:
|
||||
|
||||
ls, cp, rm, mv
|
||||
|
||||
#### 文件内容查阅:
|
||||
|
||||
cat, tac, nl, more, less, head, tail, od, file
|
||||
change file timestamps:
|
||||
touch
|
||||
|
||||
#### 修改系统隐藏权限:
|
||||
|
||||
lsattr, chattr
|
||||
|
||||
#### 默认权限:
|
||||
|
||||
umask
|
||||
|
||||
#### 文件特殊权限:
|
||||
|
||||
SUID
|
||||
SGID
|
||||
SBIT
|
||||
|
||||
#### 命令与文件搜寻:
|
||||
|
||||
which, type, whereis, locate, find
|
||||
|
||||
#### 重定向
|
||||
|
||||
##### 背景知识
|
||||
|
||||
0,1,2分别代表stdin,stdout,stderr
|
||||
|
||||
##### 基本用法
|
||||
|
||||
1. `<`重定向输入
|
||||
2. `>`重定向输出
|
||||
3. `>!`如果存在则覆盖
|
||||
4. `>&`屏幕输出的所有信息都放在指定文件中
|
||||
5. `>>`追加到文件中
|
||||
6. `>>&`屏幕上的信息追加到文件中
|
||||
|
||||
##### 用法示例
|
||||
|
||||
```shell
|
||||
# 将屏幕输出的所有信息都放在文件test.log中
|
||||
test.sh >& test.log
|
||||
```
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
#### 基本格式
|
||||
* 第1行总是以`#!/bin/sh`开始,通知shell使用何种解释器。
|
||||
* 以`#`开头的行是注释,建议在第二行注释中写入脚本名。
|
||||
* 脚本按从上到下的顺序执行。
|
||||
|
||||
#### 保留字和元字符
|
||||
|
||||
```
|
||||
do, done, for, while
|
||||
if, then, else
|
||||
```
|
||||
|
||||
```
|
||||
.. # 上级目录
|
||||
. # 当前目录
|
||||
* # 任意多个字符
|
||||
? # 任意一个字符
|
||||
~ # 用户目录
|
||||
$ # 变量参数
|
||||
# # 注释
|
||||
| # 管道
|
||||
& # 后台执行
|
||||
&& # 左边的命令返回真后,右边的命令才能执行
|
||||
|| # 左边的命令执行错误,则执行右边的命令
|
||||
() # 在当前shell执行括号内的一组命令
|
||||
{} # 在子shell执行括号内的一组命令
|
||||
[...] # 匹配括号内的任意一个字符
|
||||
[!...] # 不包含括号内的所有字符
|
||||
> # 输出重定向
|
||||
< #
|
||||
>> # 追加
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 条件控制
|
||||
|
||||
1. if-then-else
|
||||
```
|
||||
if 条件1
|
||||
then 命令1 //如果条件1为真,则执行命令1
|
||||
elif 条件2
|
||||
then 命令2 //如果条件1为假且条件2为真,则执行命令2
|
||||
else 命令3 //如果条件1为假且条件2为假,则执行命令3
|
||||
fi //fi意为if语句终止
|
||||
```
|
||||
其中,elif和else为可选项。
|
||||
2. case
|
||||
```
|
||||
case 值 in
|
||||
模式1) 命令1
|
||||
;;
|
||||
模式2) 命令2
|
||||
;;
|
||||
|
||||
...
|
||||
|
||||
*) 命令n
|
||||
;;
|
||||
esac
|
||||
```
|
||||
将值和每一个模式匹配,若匹配则执行相应命令不再与其它模式匹配,否则执行星号后的命令。
|
||||
|
||||
#### 循环
|
||||
1. for
|
||||
```
|
||||
for 变量名 in 列表
|
||||
do
|
||||
命令1
|
||||
命令2
|
||||
...
|
||||
done
|
||||
```
|
||||
2. until
|
||||
```
|
||||
until 条件
|
||||
命令1
|
||||
...
|
||||
done
|
||||
```
|
||||
当条件为真时循环结束,注意测试发生在**循环末尾**。
|
||||
3. while
|
||||
```
|
||||
while 条件
|
||||
do
|
||||
命令1
|
||||
...
|
||||
done
|
||||
```
|
||||
4. break
|
||||
|
||||
跳出循环或case语句
|
||||
5. continue
|
||||
|
||||
跳过当前循环
|
||||
|
||||
#### shell变量
|
||||
|
||||
##### 本地变量
|
||||
|
||||
- 仅对当前进程有效
|
||||
|
||||
##### 环境变量
|
||||
|
||||
- 对所有进程有效
|
||||
|
||||
- export导出环境变量
|
||||
- env显示环境变量
|
||||
- unset清除环境变量
|
||||
|
||||
##### 位置变量
|
||||
|
||||
- 用$+数字表示传递给脚本的参数
|
||||
|
||||
特定变量
|
||||
|
||||
```
|
||||
!$ # 上条命令的最后一个参数
|
||||
```
|
||||
|
||||
|
||||
|
||||
`$#, $*, $$, $!, $@, $-, $?`
|
|
@ -0,0 +1,161 @@
|
|||
#### 简介
|
||||
|
||||
awk是一种语言,用于处理文本文件。这个名字来源于三个创始人的首字母。
|
||||
|
||||
#### 语法
|
||||
|
||||
```bash
|
||||
awk [options] -f <script-file> [--] file // 用脚本处理文件
|
||||
awk [options] [--] <script> file // 用命令处理文件
|
||||
```
|
||||
#### 选项
|
||||
|
||||
```bash
|
||||
-f, --file # 指定要执行命令的脚本
|
||||
-F, --field-separator # 指定域的分割符(即预定义变量FS的值)
|
||||
```
|
||||
|
||||
#### AWK程序执行
|
||||
|
||||
```aw
|
||||
@include "filename"
|
||||
@load "filename"
|
||||
pattern {action statement}
|
||||
function name(parameter list) {statements}
|
||||
```
|
||||
|
||||
#### 变量、记录和域
|
||||
|
||||
##### 记录
|
||||
|
||||
一般来说,一行就是一条记录。但也可以通过修改变量`RS`来决定划分记录的方式。
|
||||
|
||||
##### 域
|
||||
|
||||
记录再分割就是域,`FS`是域的划分规则。
|
||||
|
||||
\$1, \$2等代表了域的编号,而\$0则代表了记录本身。
|
||||
|
||||
变量`NF`代表了一个记录里域的数量。
|
||||
|
||||
##### 内建变量
|
||||
|
||||
FILENAME : 当前输入文件的名字。
|
||||
|
||||
FS : 输入域的分割符,默认为空格。
|
||||
|
||||
NF : 当前输入记录里域的数量。
|
||||
|
||||
NR : 当前处理的是第几条记录。
|
||||
|
||||
OFMT : 数字的输出格式,默认为`%.6g`。
|
||||
|
||||
OFS : 输出域的分隔符,默认为空格。
|
||||
|
||||
ORS : 输出记录的分隔符,默认为换行符。
|
||||
|
||||
RS : 输入记录的分隔符,默认为换行符。
|
||||
|
||||
#### 模式和活动
|
||||
|
||||
模式在前,活动在后。
|
||||
|
||||
##### 模式
|
||||
|
||||
```
|
||||
BEGIN
|
||||
END
|
||||
BEGINFILE
|
||||
ENDFILE
|
||||
/正则表达式/ # 只有满足正则表达式的输入记录才会被执行
|
||||
关联表达式
|
||||
模式 && 模式 # 逻辑与
|
||||
模式 || 模式 # 逻辑或
|
||||
模式 ? 模式 : 模式 # 第1个模式为真则使用第2个模式,第1个模式为假则使用第3个模式
|
||||
(模式)
|
||||
! 模式 # 逻辑非
|
||||
模式1, 模式2 # 从满足模式1的记录开始,到满足模式2的记录结束
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### 正则表达式
|
||||
|
||||
##### 活动
|
||||
|
||||
活动是花括号之间的语句`{statements}`。
|
||||
|
||||
##### 操作符
|
||||
|
||||
##### 控制语句
|
||||
|
||||
```
|
||||
if (condition) statement [else statement]
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### I/O语句
|
||||
|
||||
```
|
||||
print # 打印当前记录
|
||||
print expr-list # 打印表达式expr-list
|
||||
print expr-list > file # 将打印出来的表达式重定向到文件
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### printf语句
|
||||
|
||||
##### 特殊文件名
|
||||
|
||||
##### 数字函数
|
||||
|
||||
```
|
||||
cos(expr) # 余弦,expr是弧度值
|
||||
rand() # 返回一个0到1之间的随机数
|
||||
sin(expr) # 正弦,expr是弧度值
|
||||
sqrt(expr) # 平方根
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### 字符串函数
|
||||
|
||||
```
|
||||
length(str) # 返回字符串的长度
|
||||
tolower(str) # 字符转化为小写
|
||||
toupper(str) # 字符转化为大写
|
||||
substr(s,i,[,n]) # 返回从第i的字符开始,最多n个字符的子串。如果不给定n,则返回字符串全部剩余的部分
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### 时间函数
|
||||
|
||||
##### 位处理函数
|
||||
|
||||
##### 国际化函数
|
||||
|
||||
#### 用户定义的函数
|
||||
|
||||
#### 动态加载新函数
|
||||
|
||||
#### 信号
|
||||
|
||||
#### 国际化
|
||||
|
||||
#### POSIX兼容
|
||||
|
||||
#### GNU扩展
|
||||
|
||||
#### 环境变量
|
||||
|
||||
#### 退出状态
|
||||
|
||||
#### 例子
|
||||
|
||||
```bash
|
||||
awk '{print $0}' demo.txt # 打印demo.txt的内容
|
||||
```
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
#### 简介
|
||||
|
||||
#### 选项
|
||||
|
||||
#### 参数
|
||||
|
||||
#### 调用
|
||||
|
||||
#### 定义
|
||||
|
||||
#### 保留字
|
||||
|
||||
#### 语法
|
||||
|
||||
#### 注释
|
||||
|
||||
#### 引用
|
||||
|
||||
#### 参数
|
||||
|
||||
#### 扩展
|
||||
|
||||
#### 重定向
|
||||
|
||||
#### 别名
|
||||
|
||||
#### 函数
|
||||
|
||||
#### 算术求值
|
||||
|
||||
#### 条件表达式
|
||||
|
||||
#### 简单命令扩展
|
||||
|
||||
#### 命令执行
|
||||
|
||||
#### 命令执行环境
|
||||
|
||||
#### 环境
|
||||
|
||||
#### 退出状态
|
||||
|
||||
#### 信号
|
||||
|
||||
#### 作业控制
|
||||
|
||||
#### READLINE
|
||||
|
||||
#### shell内建命令
|
||||
|
||||
```
|
||||
pushd [dir] # 指定目录栈的栈顶,并切换到栈顶所指的目录;如果不加任何参数,则交换最顶端两个参数的位置。
|
||||
# -n参数只将目录加入栈中,而不切换目录
|
||||
# +N则会把第N个目录加入栈顶(从左边数起,从0开始)
|
||||
# -N则会把第N个目录加入栈顶(从右边数起,从0开始)
|
||||
# dir参数用于将dir加到栈顶并切换到dir
|
||||
popd # 删除目录栈中的某个目录;如果不加任何参数,则删除栈顶的目录,并切换到新栈顶所指的目录。
|
||||
# -n参数只删除栈顶的目录,而不切换目录
|
||||
# +N则会删除第N个目录(从左边数起,从0开始)
|
||||
# -N则会删除第N个目录(从右边数起,从0开始)
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 受限的shell
|
|
@ -0,0 +1,6 @@
|
|||
```shell
|
||||
md5sum
|
||||
sh1sum
|
||||
sha256sum
|
||||
sha512sum
|
||||
```
|
|
@ -0,0 +1 @@
|
|||
chgrp //更改文件所属群组
|