@ -6,7 +6,7 @@
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					RISC-V( ) ( , ) ( , ) , , ( ) ( , ) , , , , , ( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们认为, 相比于传统的商用指令集架构, :  
			
		
	
		
			
				
					相比于传统的商用指令集架构, : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					● ** 后发优势**。RISC-V指令集的提出, , , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -55,21 +55,21 @@ RISC-V( ) ( 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					**1.2.2 指令格式**
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.3 列出了RV64I( ) 
 
			
		
	
		
			
				
					图1.1 列出了RV64I( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					  
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.2  RV64I的常用基础指令, 
 
			
		
	
		
			
				
					图1.1  RV64I的常用基础指令, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					从图1.3 中, , ( ) ( ) ( , ) , , , , , , , , 
 
			
		
	
		
			
				
					从图1.1 中, , ( ) ( ) ( , ) , , , , , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					RISC-V汇编指令的格式与8086汇编是截然不同的。以最基础的加法指令为例, ( ) : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					add rd, rs1, rs2
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					或者
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					addi rd, rs1, immediate
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					其中, ( ) , ( ) , ( ) , ; , , ( ) , , ( , ) , ( ) , , ( ) 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -81,25 +81,25 @@ addi rd, rs1, immediate
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们以ld( ) ( ) , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					ld rd, offset(rs1)
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					执行ld指令的动作是将立即数offset进行符号扩展, , , ( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					以下是sw指令的语法: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					sw rs2, offset(rs1)
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					它的作用是将立即数offset进行符号扩展, , , ( , , , ) ( ) , , ( ) : , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					以上的例子中, , , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					[0x0000 0000 0000 0800] = 0xCD
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					[0x0000 0000 0000 0801] = 0xAB
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					[0x0000 0000 0000 0802] = 0x34
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					[0x0000 0000 0000 0803] = 0x12
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -111,19 +111,19 @@ sw rs2, offset(rs1)
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					基本行内汇编很容易理解,一般是按照下面的格式:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					asm(“statements”);
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					该语句中“asm”也可以由“__asm__”来代替。在“asm”后面有时也会加上“__volatile__”表示编译器不要对括弧内的汇编代码进行任何优化, : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					asm(“li x17,81”);      //将立即数81存入x17寄存器
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					asm(“ecall”);         //调用ecall指令( ) 
 
			
		
	
		
			
				
					` ( ) `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					编译器碰到以上语句后, , ( ) , , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					asm(“li x17,81\n\t”
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  “ecall”);
 
			
		
	
		
			
				
					  ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					也就是采用分隔符“\n\t”隔开多条汇编指令。对于编译器而言, , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -131,17 +131,17 @@ asm(“li x17,81\n\t”
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					扩展内联汇编使得嵌入在C语言中的代码能够带输入、输出参数, , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					asm volatile( 
 
			
		
	
		
			
				
					` `   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					"statements"(汇编语句模板): 
 
			
		
	
		
			
				
					` `   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					output_regs( ) 
 
			
		
	
		
			
				
					` ( ) `   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					input_regs( ) 
 
			
		
	
		
			
				
					` ( ) `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					clobbered_regs( ) 
 
			
		
	
		
			
				
					` ( ) `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					) ; 
 
			
		
	
		
			
				
					` ; `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					其中asm 表示后面的代码为内嵌汇编, ; , ; , ; , ; , ( ) , , : 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -164,23 +164,23 @@ clobbered_regs( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们来看一个C语言内嵌汇编代码的例子: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					int dest=0;
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					int value=1;
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					asm volatile (
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					   "lw t0,%1 \n\t"
 
			
		
	
		
			
				
					   ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					   "add t0,t0,t0 \n\t"
 
			
		
	
		
			
				
					   ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					   "sd t0,%0"
 
			
		
	
		
			
				
					   ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					   :"=m"(dest)       //输出部分
 
			
		
	
		
			
				
					   ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					   :"m" (value)      //输入部分
 
			
		
	
		
			
				
					   ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					   : "memory");      //破坏描述部分
 
			
		
	
		
			
				
					   ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					在这个例子定义了两个局部变量dest和value, , ; ( ) ; , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -190,57 +190,59 @@ asm volatile (
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们再看一个稍微大一点的例子, , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 1 #include  < stdio.h > 
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					 `1 #include <stdio.h>` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 2
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 3 void bar()
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 4 {
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 5   asm volatile( "li s5, 300" );
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 6 }
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 7
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 8 int foo( int foo_arg )
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 9 {
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 10   int x;
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 11   asm volatile( "li s5, 500" );
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 12   bar();
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 13   asm volatile (
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 14      "sd s5,%0"
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 15      :"=m"(x)
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 16      :
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 17      : "memory");
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 18   printf( "x=%d\n", x );
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 19   return 10;
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 20 }
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 21
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 22 int main()
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 23 {
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 24   foo( 10 );
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 25   return 0;
 
			
		
	
		
			
				
					 ` ` 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 26 }
 
			
		
	
		
			
				
					 `26 }` 
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					例1.1 test_asm.c代码列表
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -248,20 +250,21 @@ asm volatile (
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					将例1.1采用RISC-V交叉编译器编译为二进制代码, : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					riscv64-unknown-elf-gcc test_asm.c -march='rv64g' -o test
 
			
		
	
		
			
				
					`$ `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					编译时我们通过GCC的“-march”开关指定目标指令集是RV64G。接下来采用以下命令执行例1.1中的代码( , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ spike pke test
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					以上命令的执行结果为“x=300”。
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					这说明编译器为了代码的效率, : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					riscv64-unknown-elf-objdump -D ./test | less
 
			
		
	
		
			
				
					`$ `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					得到以下输出:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					00000000000101a4 < bar > :
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  101a4:    ff010113        addi  sp,sp,-16
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -347,14 +350,15 @@ riscv64-unknown-elf-objdump -D ./test | less
 
			
		
	
		
			
				
					  1023c:    01010113        addi  sp,sp,16
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  10240:    00008067        ret
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					输出的第一列是我们的程序地址( ) ; , ( ) , ; ( ) , ( ) , ( ) , ( ) , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们仍然以例1.1为例分析RISC-V程序执行过程中, , , , , , ( ) ; ( , ) , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					 < img  src = "pictures/fig1_2.png"  alt = "fig1_2"  style = "zoom:80%;"  / > 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.3  例1.1程序的函数调用栈结构( ) 
 
			
		
	
		
			
				
					图1.2  例1.1程序的函数调用栈结构( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们看到, ( ) , , ( ) , , , , , , , , ( ) , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -364,9 +368,9 @@ riscv64-unknown-elf-objdump -D ./test | less
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					实际上, , , , ( ) ( ) ; ( ) , ( ) ; ( , ) , , ( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					 < img  src = "pictures/fig1_3.png"  alt = "fig1_3"  style = "zoom:80%;"  / > 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.4  RISC-V机器的特权模式与特权级转换
 
			
		
	
		
			
				
					图1.3  RISC-V机器的特权模式与特权级转换
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					通过《操作系统原理》课我们知道,**现代的处理器定义不同特权级的根本原因,是为了对操作系统进行保护**。例如, , , , ( , ) , ( ) , , , ( ) , , , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -393,9 +397,9 @@ RISC-V为每一种特权模式定义了一组寄存器, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					表1.3中的寄存器很多只是存放了一个指向内存地址的指针( ) , ; , , , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					  
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.5  mstatus寄存器结构
 
			
		
	
		
			
				
					图1.4  mstatus寄存器结构
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					以下是对mstatus中各个位的说明: 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -440,13 +444,13 @@ SUM( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					这些寄存器的长度实际上取决于M模式下mstatus寄存器中的SXL位的值。但为了简化后续讨论, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					比较表1.3和1.4, , , , , 6 所示:
 
			
		
	
		
			
				
					比较表1.3和1.4, , , , , 5 所示:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					  
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.6  sstatus寄存器
 
			
		
	
		
			
				
					图1.5  sstatus寄存器
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					相比于图1.5中的mstatus,  中的sstatus像是一个“缩水”版的mstatus。sstatus中与mstatus相同的位具有与mstatus模式中相同的作用( ) , , , , , 
 
			
		
	
		
			
				
					相比于图1.4中的mstatus,  中的sstatus像是一个“缩水”版的mstatus。sstatus中与mstatus相同的位具有与mstatus模式中相同的作用( ) , , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					相应的, , , , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -484,11 +488,11 @@ SUM( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					实际系统中导致中断发生的事件往往是比较复杂的,它们的来源、处理时机和返回方式都不尽相同。为了便于读者对中断的理解以及表达的准确性,我们借鉴参考文献[SiFive Interrupt]的中断分类标准, : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					● **Exception** (异常):这类中断是处理器在执行某条指令时,由于条件不满足而产生的。典型的异常有除零错误、缺页、执行当前特权级不支持的指令等。**相对于正在执行的程序而言,**** exception**** 是同步(**** synchronous**** )发生的。**** exception**** 产生的时机是指令执行的过程中(即处理器流水线的执行阶段),在**** exception**** 处理完毕后,系统将返回发生**** exception**** 的那条指令重新执行**。
 
			
		
	
		
			
				
					● **Exception** (异常):这类中断是处理器在执行某条指令时,由于条件不满足而产生的。典型的异常有除零错误、缺页、执行当前特权级不支持的指令等。**相对于正在执行的程序而言, ( ) ( ) , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					● **Trap** ( , ! ) : ( ) ( ) ( ) , ( ) , **** exception**** 类似,相对于正在执行的程序而言,**** trap**** 也是同步(**** synchronous**** )发生的。但与**** exception**** 不同的地方在于,**** trap**** 在处理完成后返回的是下一条指令。**
 
			
		
	
		
			
				
					● **Trap** ( , ! ) : ( ) ( ) ( ) , ( ) , , , ( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					● **Interrupt** ( ! ) : , ( ) : ( ) **** interrupt**** 是异步(**** asynchronous**** )发生的。另外,对于处理器流水线而言,**** interrupt**** 的处理时机是指令的间隙。不同于**** exception**** 但与**** trap**** 类似,**** interrupt**** 在处理完成后返回的是下一条指令**。
 
			
		
	
		
			
				
					● **Interrupt** ( ! ) : , ( ) : ( ) , ( ) , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					表1.6 中断的分类
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
				@ -582,39 +586,25 @@ SUM( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					典型的中断处理流程如以下代码:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					.align 6
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					.global handler_interrupt
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					handler_interrupt:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					addi sp, sp, -32*REGBYTES
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					STORE x1, 1* REGBYTES(sp)
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					STORE x2, 2* REGBYTES(sp)
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					...
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					STORE x31, 31* REGBYTES(sp)
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					//call C code handler
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					call software_handler
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					//finished interrupt handling, ready to return
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					LOAD x1, 1* REGBYTES(sp)
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					LOAD x2, 2* REGBYTES(sp)
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					...
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					LOAD x31, 31* REGBYTES(sp)
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					addi sp, sp, 32*REGBYTES
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					mret
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					在以上的代码中, ( , ) , , , ( ) 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -636,9 +626,10 @@ RISC-V在中断处理上有一个很有意思的设计, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					例如, , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					csrw mideleg, 1< < 1  |  1 < < 5  |  1 < < 9 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					csrw medeleg, 1< < 0  |  1 < <  3  |  1 < < 8  |  1 < < 12  |  1 < < 13  |  1 < < 15  
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					这段代码的作用是将M模式中interrupt中的1、5和9号, , , ; ( ) , , ( ) , ( ) , ( ) , , , , , , ( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -658,7 +649,7 @@ csrw medeleg, 1<<0 | 1<< 3 | 1<<8 | 1<<12 | 1<<13 | 1<<15
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					接下来我们来讨论逻辑地址。对于Sv39而言, ( , , ) ! , : , ! , ( , ) , , ! , , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					逻辑地址空间大小 ≠ 物理地址空间大小
 
			
		
	
		
			
				
					** 逻辑地址空间大小 ≠ 物理地址空间大小**  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					应该来说, , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -668,21 +659,21 @@ csrw medeleg, 1<<0 | 1<< 3 | 1<<8 | 1<<12 | 1<<13 | 1<<15
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们来分析Sv39虚存管理方案中逻辑地址到物理地址的转换。由于Sv39是一个页式虚存管理方法, ( ) ( , ) , , ( ) ( ) , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					接下来, , 7 所示
 
			
		
	
		
			
				
					接下来, , 6 所示
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					  
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.7  Sv39中逻辑地址的结构
 
			
		
	
		
			
				
					图1.6  Sv39中逻辑地址的结构
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					从图1.7 中, , : ( ) ( ) ( ) ( ) ( , ) , ( , ) 
 
			
		
	
		
			
				
					从图1.6 中, , : ( ) ( ) ( ) ( ) ( , ) , ( , ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					页内偏移为12位, : , , ? ( ) ( ) , ( , ) ( , ) ( ) ! , 8 中PTE或PDE的格式。
 
			
		
	
		
			
				
					页内偏移为12位, : , , ? ( ) ( ) , ( , ) ( , ) ( ) ! , 7 中PTE或PDE的格式。
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					  
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.8  Sv39中PDE/PTE格式
 
			
		
	
		
			
				
					图1.7  Sv39中PDE/PTE格式
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					对图1.8 中PDE/PTE格式的解释: 
 
			
		
	
		
			
				
					对图1.7 中PDE/PTE格式的解释: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					● V( ) ( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -700,17 +691,17 @@ csrw medeleg, 1<<0 | 1<< 3 | 1<<8 | 1<<12 | 1<<13 | 1<<15
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					● PPN( ) ( , ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.8 的PDE/PTE格式中有一个很重要的问题: ? , , ? ! , , , ( ) 
 
			
		
	
		
			
				
					图1.7 的PDE/PTE格式中有一个很重要的问题: ? , , ? ! , , , ( ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					在知道Sv39的逻辑地址结构以及PDE/PTE格式后, , 9 所示:
 
			
		
	
		
			
				
					在知道Sv39的逻辑地址结构以及PDE/PTE格式后, , 8 所示:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					  
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.9  Sv39中虚拟地址到物理地址的转换过程
 
			
		
	
		
			
				
					图1.8  Sv39中虚拟地址到物理地址的转换过程
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					地址变换机构首先获得逻辑地址va的VPN[2], ( ) , ; , ; , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					**需要注意的是,图****1.9****中所示的地址变换过程是由****RISC-V**** 处理器硬件完成的,但是页表的构造却是操作系统完成的!**对于系统中运行的每个进程( ) , , , , ( ) , ( ! ) , ( ) , 
 
			
		
	
		
			
				
					**需要注意的是,图1.8中所示的地址变换过程是由RISC-V 处理器硬件完成的,但是页表的构造却是操作系统完成的!**对于系统中运行的每个进程( ) , , , , ( ) , ( ! ) , ( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					将一个进程的( ) , , , , , ! , , ( ) , , ( ) , , , ( ) 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -718,11 +709,11 @@ csrw medeleg, 1<<0 | 1<< 3 | 1<<8 | 1<<12 | 1<<13 | 1<<15
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					**1.5.3 satp、Sv48、TLB和非基础页**
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					在图1.9的虚拟地址变换中, ,  所示。
 
			
		
	
		
			
				
					在图1.8的虚拟地址变换中, ,  所示。
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					  
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.10  satp寄存器格式
 
			
		
	
		
			
				
					图1.9  satp寄存器格式
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					satp寄存器包含一个44位的PPN, ; ( ) , , , ( , ) , ; , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -748,91 +739,89 @@ satp寄存器包含一个44位的PPN, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					PKE的实验将涉及Linux环境下较多工具软件的使用, , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					● git
 
			
		
	
		
			
				
					 ● git
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					git是一个开源的分布式版本控制系统, , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					git init
 
			
		
	
		
			
				
					`$ `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					对已存在文件进行版本控制
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					git add *.c
 
			
		
	
		
			
				
					`$ `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					git add LICENSE
 
			
		
	
		
			
				
					`$ `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					git commit -m 'Initial project version'
 
			
		
	
		
			
				
					`$ `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					对于一个远程的git仓库, , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					git clone url
 
			
		
	
		
			
				
					`$ `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					当clone完成, , , : , , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					 < img  src = "pictures/fig1_10.png"  alt = "fig1_10"  style = "zoom:60%;"  / > 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					图1.11  git仓库中文件的状态转换
 
			
		
	
		
			
				
					图1.10  git仓库中文件的状态转换
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					使用git status可以查看目录下的文件状态
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git status
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					使用add命令可以递归地跟踪该目录下的所有文件、或暂存已修改的文件
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git add fileName/directory
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					使用commit命令可以提交更新
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git commit -m "commit information"
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					◇ 分支相关命令
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					查看分支:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git branch
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					本地创建远端分支dev( , ) : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git checkout -b dev origin/de
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					合并分支dev: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git merge dev
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					本地删除分支dev: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git branch -D/d dev
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					切换到已有分支dev: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git checkout dev
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					本地创建并切换到分支dev: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git checkout -b dev
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					推送分支dev到远端: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git push origin
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					◇ 调试相关命令
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git log
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					显示每次提交提交日期的差异,-2选项来只显示最近的2次提交: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git log -p -2
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					每次提交的简略统计信息:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git log --stat
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					格式化log输出: 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git log --pretty=oneline [short、full、fuller]//用一行显示每一次提交
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ git log --pretty=format:"%h - %an, %ar : %s"
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -854,19 +843,19 @@ $ git log --pretty=format:"%h - %an, %ar : %s"
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					\1. -o选项指定输出文件名称为hello
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ riscv64-unknown-elf-gcc  hello.cpp  -o hello
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					\2. 使用-E选项, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ riscv64-unknown-elf-gcc -E hello.cpp  -o hello.i
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					\3. 使用-S选项, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ riscv64-unknown-elf-gcc -S hello.cpp -o hello.s
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					\4. 使用-c选项, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ riscv64-unknown-elf-gcc -c hello.s  -o hello.o
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -884,77 +873,53 @@ $ riscv64-unknown-elf-gcc -c hello.s  -o hello.o
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					\1. 查看对象文件所有的节sections.
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$riscv64-unknown-elf-objdump -h  elf_file 
 
			
		
	
		
			
				
					` `   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					输出如下:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					hello_elf:   file format elf64-littleriscv
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					Sections:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					Idx Name     Size   VMA        LMA        File off Algn
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 0 .text     000024cc 00000000000100b0 00000000000100b0 000000b0 2**1
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 1 .rodata    0000000a 0000000000012580 0000000000012580 00002580 2**3
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 2 .eh_frame   00000004 000000000001358c 000000000001358c 0000258c 2**2
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 3 .init_array  00000010 0000000000013590 0000000000013590 00002590 2**3
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 4 .fini_array  00000008 00000000000135a0 00000000000135a0 000025a0 2**3
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 5 .data     00000f58 00000000000135a8 00000000000135a8 000025a8 2**3
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 6 .sdata    00000040 0000000000014500 0000000000014500 00003500 2**3
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 7 .sbss     00000020 0000000000014540 0000000000014540 00003540 2**3
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 8 .bss     00000068 0000000000014560 0000000000014560 00003540 2**3
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 9 .comment   00000011 0000000000000000 0000000000000000 00003540 2**0
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 10 .riscv.attributes 00000035 0000000000000000 0000000000000000 00003551 2**0
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					可以看得一个section在文件中对应的属性有六个, , , , , , 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					\2. 显示文件的摘要信息, 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$riscv64-unknown-elf-objdump -f elf_file 
 
			
		
	
		
			
				
					` `   
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					例如我们使用: , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					hello_elf:   file format elf64-littleriscv
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					architecture: riscv:rv64, flags 0x00000112:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					EXEC_P, HAS_SYMS, D_PAGED
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					start address 0x00000000000100c2
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -964,7 +929,7 @@ start address 0x00000000000100c2
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					编译汇编源文件到目标文件,例如:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ riscv64-unknown-elf-as hello.S -o hello.o
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -974,11 +939,13 @@ $ riscv64-unknown-elf-as hello.S -o hello.o
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					file命令用于辨识文件类型, : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ file app/elf/app1
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们将看到以下输出:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					app/elf/app1: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, with debug_info, not stripped
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					表明app/elf/app1文件是一个可执行的二进制代码( ) , 
 
			
		
	
		
			
				
					
 
			
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
				@ -996,21 +963,23 @@ app/elf/app1: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), staticall
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					spike是RISC-V仿真器, , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ spike pke  helloworld
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					得到输出如下:
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					Hello World! 
 
			
		
	
		
			
				
					```
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					spike默认提供的内存为2048MiB, ( ) , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ spike -m1024  pke  helloworld
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					就可以将模拟出来的RISC-V机器的内存大小指定为1024MiB。
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					RISC-V是一个可扩展指令集, , , : 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					$ spike --isa=RV64GCV  pke  helloworld
 
			
		
	
		
			
				
					` `  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					我们将目标机器的ISA指定为RV64GCV。