课程主页
Starter Code For Lab & Project
文章目录
- Lec 1 Intro, Number Representation
- Lec 2 C Intro – Basics
- Disc01 Number Representation
- Lab 0: Intro and Setup
- Project 1: Philphix
- Lec 3 C Intro - Pointers, Arrays, Strings
- Lec 4 C Memory Management
- Disc02 C Basics
- Lab 1: Number Rep, C, and CGDB
- Lec 5 Floating Point
- Lec 6 Intro to Assembly Language, RISC-V Intro
- Disc03 Floating Point
- Lec 7 RISC-V, RISC-V Functions
- Lec 8 RISC-V Instruction Formats
- Lab 2: Advanced C
- Disc04 RISC-V Intro, RISC-V Control Flow, ISA
- Lab 3: RISC-V Assembly
- Lec 9 Compiler, Assembler, Linker, Loader (CALL)
- Lec 10 Introduction to Synchronous Digital Systems (SDS)
- Disc05 CALL, RISC-V Procedures
- Lec 11 Functional Units, FSMs
- Lab 4: RISC-V Functions, Pointers
- Lec 12 RISC-V Datapath, Single-Cycle Control Intro
- Disc06 SDS, Logic, FSM
- Project 2: CS61Classify
- Lec 13 RISC-V Single-Cycle Control
- Lec 14 RISC-V 5-Stage Pipeline/Hazards
- Disc07 Single-Cycle Datapath
- Lec 15 Memory Hierarchy, Fully Associative Caches
- Lab 5: Logisim
- Disc08 Pipelining and Hazards
- Lab 6: Pipelining and CPU, Mid-Semester Survey
- Lec 17 Mutilevel Caches, Cache Questions
- Disc09 Caches & AMAT
- Lab 7: Caches
- Project 3: CS61CPU
- Lec 18 OS
- Lec 19 Virtual Memory
- Lec 20 IO
- Disc10 OS & Virtual Memory
- Lab 8: OS (context switch), I/O, DMA, Disks, Networking & Virtual Memory
- Disc11 I/O
- Lab 10: Thread Level Parallelism
- The Rest
- 课程总结
Lec 1 Intro, Number Representation
数字电路基础,采样、量化和编码,二进制、十进制和十六进制之间的相互转换;
原码、反码、补码和偏置编码的基本概念。
Lec 2 C Intro – Basics
C语言语法基础
Disc01 Number Representation
复习了Lec 1所讲的知识点,略过。
Lab 0: Intro and Setup
课程政策、相关的服务(Gradescope, Hive machines, Piazza之类的,基本上用不到)、Linux和git相关命令(有一定参考价值)。
Project 1: Philphix
参考CS61C Spring 2021——Project 1: Philphix要求及实现思路
Lec 3 C Intro - Pointers, Arrays, Strings
指针:初始化、取地址和解引用
C和Java按值传递参数——如果变量是指针,那么函数就将获取该地址进行相关的操作。
C中数组也是指针,始终指向数组中的第一个元素。
handle:句柄,指向指针的指针。
Lec 4 C Memory Management
使用malloc()函数动态分配内存:
ptr = (int*) malloc(sizeof(int))
/* 在分配完之后,还需要检查其是否为空ptr == NULL */
使用realloc()函数重新调整分配内存空间的大小:
ptr = (int*) realloc(ptr, 2 * sizeof(int))
/* 在分配完之后,还需要检查其是否为空ptr == NULL */
malloc/realloc返回的是指向泛型空间(void)的指针,所以需要使用类型转换(typecast)。
在使用完分配的内存后,还应该释放它,否则容易造成内存泄漏(memory leak):
free(ptr) /* free和malloc/realloc经常成对出现 */
C内存管理——stack(栈)&heap(堆)
malloc和realloc分配的是heap中的空间,因此当想要分配的空间过大时,分配就会失败,返回NULL,因此在malloc/realloc之后需要check。
Note:如果想在malloc的同时将申请的内存初始化为0,可以使用calloc
void* calloc(size_t num, size_t size); /* num为元素个数,size为每个元素的大小 */
Disc02 C Basics
练习指针、内存管理等内容的使用。
Lab 1: Number Rep, C, and CGDB
Setup
在windows下安装WSL,参考Windows Subsystem for Linux(WSL)的安装、美化和增强
安装完成后执行如下命令,安装C编译器:
sudo apt install build-essential cgdb valgrind
Debug
使用txt文件作为cGDB运行时的输入(参考链接):
run paramas stdin < filename.txt
Exercise 6: Pointers and Structures in C
此次练习需要我们补充单链表的循环检测算法——龟兔算法,简单来说就是使用两个指针A和B来检测链表中是否存在循环。其中A(乌龟)每次前进一个节点,B(兔子)每次前进两个节点。如果链表中存在循环的话,A和B最终会在同一个节点相遇。
那为什么A和B会在同一个节点相遇呢?如果存在循环,A和B最终都会在循环中前进,B每次比A多走一个节点(B与A的距离每次减一),因此不管它们相距多少个节点,只要在循环中,B最终都会和A相遇。
Lec 5 Floating Point
小数的表示方法
使用上面所示的标准化方法来表示小数,小数点左边总是有一个1,使用指数来控制小数点的移动。
在计算机中存储的格式:
这种方式存在局限,使用8bit存储指数位,永远都是在将小数点往右移动,无法表示更接近0的数。8bit可以表示的范围为0—255,使用偏置,减去127,使它可以表示-127—128之间的数(小数点可以向左/向右移动基本相同的范围)。
IEEE 754浮点数标准:
Note:
Overflow——超过了所能表示的最大的正数/最小的负数;
Underflow——超过了所能表示的最小精度,更接近于0。
实际上对这种表示方法各个部分的功能,还有更为精确的划分:
Note:
denorm部分不采用标准格式中的前导1,而使用0,用来表示更小的精度(更接近0的部分)。
IEEE 754 Converter——在线工具,方便理解754标准;
双精度(double precision)——使用64bit来表示小数,表示有效位的bit也将变多,精度提升,因此被称为双精度。
Lec 6 Intro to Assembly Language, RISC-V Intro
RISC(Reduced Instruction Set Computer):精简指令集架构,指令集更为简单,通过不同指令的组合来完成相应的操作。
RISC-V的一些指令介绍,略。
Disc03 Floating Point
对浮点数的应用,判断正误、数值转换等题目,略。
Lec 7 RISC-V, RISC-V Functions
调用函数的6大基本步骤:
1.将参数放置在函数可以访问到的地方(约定好的几个寄存器);
2.跳跃到该函数所在位置(同时将返回地址存储在了寄存器中);
3.获取函数所需的本地存储资源(寄存器);
此函数需要x个寄存器,就将寄存器中的值(可能是调用此函数的程序所需的数据)存入stack中(和C语言内存管理的部分联系起来了)。每个函数在stack中开辟的新的内存空间被称为frame(帧)。此外,frame中还会存储其他寄存器的数据,嵌套函数调用的部分再引入。
4.执行函数;
5.将返回值存入调用程序可以访问的地方(寄存器中),从stack frame中恢复函数使用过的几个寄存器,销毁该stack frame(sp指针增加相应的size即可,这也是为什么这片空间被称为stack(栈))。
嵌套函数调用
事实上,大部分的函数调用都是嵌套的,main函数调用func 1,func 1再调用func 2。在调用func 1的时候,我们将返回地址,func 1所使用的参数存入特定寄存器中,调用func 2的时候,这些数据都会被破坏。因此,RISC-V将寄存器分为了保存(saved)寄存器和临时寄存器,在函数调用的时候将临时寄存器中的数据存入stack,函数返回的时候再恢复。保存寄存器中存储的是函数调用过程中不会被改变的数据。
临时和保存的概念是相对caller即调用者函数而言的。
t0-t6——temporary registers,临时寄存器。之所以叫临时,是因为这些寄存器中的值在caller调用callee之后不能保证还继续存在,即有可能被callee修改/使用。因此临时寄存器需要在调用callee之前由caller函数存入stack中,调用结束之后再由caller从stack中取回。
s0-s11——saved registers,保存寄存器。即在caller调用callee的过程中存储的值不会发生改变的寄存器。之所以保存寄存器的值不会发生改变,不是因为callee没有使用这些寄存器,而是因为callee在执行函数具体功能之前将s0-s11的值存入了stack中,返回前再将stack中的值存回s0-s11。这样对于caller而言,s0-s11寄存器中的值在函数调用前后始终保持一致。
更多关于调用惯例的细节可以参考RISC-V Calling Convention。
Note:
Caller——嵌套函数中的调用函数
Calee——嵌套函数中的被调用函数
Lec 8 RISC-V Instruction Formats
每条指令都会被编码成二进制的格式,在RISC-V中,使用32bit来编码指令。RISC-V中的指令被分为了以下6种格式:
以R-format为例,32bit被分为了六个字段来进行表示:
opcode——标识指令的类型,R-format都为0110011;
rd——register destination,目标寄存器;
rs1——register source 1,源寄存器1;
rs2——register source 2,源寄存器2;
funct7+funct3——指明了执行的具体指令;
Note:之所以有opcode+funct3+funct7这些奇怪的字段,是为了让CPU更快的执行这些指令。
Lab 2: Advanced C
为什么要使用malloc分配内存空间?exercise 3中没有使用malloc分配的变量,都在stack(栈)上存储,在函数运行完成后被立即释放。而存储在heap(堆)上的变量(链表、结构体、数组之类的)在free之前都会一直存在。
.h头文件告诉其他.c文件它们可以访问的对象(变量,函数)。用<>标识的是系统文件,”“标识的是用户自定义的头文件。
Disc04 RISC-V Intro, RISC-V Control Flow, ISA
1.函数通常使用a0和a1寄存器来存储返回值;
2.caller函数可以使用a0-a7寄存器向函数传递参数;
Lab 3: RISC-V Assembly
使用如下命令将lab文件夹通过网络端口暴露给Venus:
python ./tools/venus . -dm
ecall:使用带有参数10的ecall指令来退出程序。ecall指令类似于“系统调用”并允许我们去做一些事情,比如像打印字符到控制台或者从heap中请求一片内存。
Venus中jalr的格式和标准格式有点不同,应该使用这种格式:jalr ra, rs, offset。
在后面的课程中,将处理多个汇编代码文件组成的RISC-V程序。需要阅读下面的这些材料:
Writing Larger RISC-V Programs
Using the Web Terminal
Passing Command Line Arguments
Lec 9 Compiler, Assembler, Linker, Loader (CALL)
编译和运行C程序的四个步骤(CALL):
Lec 10 Introduction to Synchronous Digital Systems (SDS)
晶体管的结构:
三极:源极(S)、栅极(G)、漏极(D)
Source:来源的地方;
Gate:类似于水龙头的开关;
Drain:字面意思就是下水道,排水管。
晶体管又可以分为两种类型,n-channel和p-channel的。n-channel里的n,可以按照normal理解,即正常思维下,G极处于高电压下时,S-D之间的通路就会打开;p-channel的正好与之相反。以前学模电的时候太死板啦,很多东西都学得很痛苦。
Disc05 CALL, RISC-V Procedures
回顾compile、assemble、link、load四个过程。
Lec 11 Functional Units, FSMs
DQ触发器(flip-flop),建立时间(setup time),保持时间(hold time),clk-to-Q延迟:
区分与门(AND)和或门(OR)的方法,有趣:
有符号数加法器原理&加法器/减法器设计原则。
补码取反的思路:以2bit为例,11取反为00,然后加1得到01(十进制数1),11为-1。
Lab 4: RISC-V Functions, Pointers
熟悉Venus,RISC-V常见bug。
Lec 12 RISC-V Datapath, Single-Cycle Control Intro
根据各种不同指令设计RISC-V CPU中的Datapath和Control,这一节主要讲的是根据各种指令类型设计Datapath。
Disc06 SDS, Logic, FSM
布尔表达式,逻辑门,建立时间,保持时间,延迟,有限状态机相关知识点复习。
Project 2: CS61Classify
参考CS61C Spring 2021——Project 2: CS61Classify要求及实现思路
Lec 13 RISC-V Single-Cycle Control
单周期控制单元的设计,ROM/组合逻辑电路。
Lec 14 RISC-V 5-Stage Pipeline/Hazards
流水线设计及缺陷解决方案。
Disc07 Single-Cycle Datapath
单周期CPU datapath练习。
Lec 15 Memory Hierarchy, Fully Associative Caches
Caches:内存子集的备份。为什么需要caches呢?CPU执行速度远大于内存的读写速度(图书馆类比,内存(图书馆藏书)越大,检索某个数据(图书)所需要花费的时间越多)。其他类似的例子,比如浏览器的书签,打开微信之后经常使用联系人页面等都使用了类似于caches的概念。
1.直接映射缓存(最简单的缓存设计方式)
将一个内存地址分为三个字段,Tag、Index、Offset(助记符TIO),很巧妙的设计。
Lab 5: Logisim
在lab文件夹的根目录,运行如下命令以运行Logisim:
./tools/logisim
Lec 16 Caches– Direct-mapped, set-associative; Program Performance w/ Caches
Set-Associative Caches,在direct map缓存中,同一颜色的memory数据不能同时出现在caches中,容易出现conflict miss,对性能有很大影响。Set-Associative Caches的每一个set可以存储来自同一颜色的数据,大大减小了conflict miss出现的可能性。此时TIO字段中的index指示的就是caches中set的位置,之后再通过比较该set中所有的tag和给出的tag,来得到想要的数据。
Disc08 Pipelining and Hazards
流水线操作及其隐患练习。
Lab 6: Pipelining and CPU, Mid-Semester Survey
一般,对流水线操作的电路细节还是不太理解,但也通过测试了。
Lec 17 Mutilevel Caches, Cache Questions
视频重复,和Lec16的部分内容一致。
Disc09 Caches & AMAT
缓存练习。
Lab 7: Caches
Caches Hit(命中) & Miss(缺失)
Project 3: CS61CPU
参考CS61C Spring 2021——Project 3: CS61CPU要求及实现思路
Lec 18 OS
OS操作系统所提供的功能:
OS中的进程和线程
进程——一个进程对应于一个正在被执行的程序实例,进程可以包含很多线程;
线程——由操作系统独立管理的单个执行序列。
进程类似于整个计算机,对于内存和CPU的计算资源消耗较大。线程则类似于CPU的不同的core,共享同一块内存空间,和进程相比资源消耗较小。正因为同一进程中的线程访问的是同一块内存空间,所以存在race condition(竞争条件)的问题,即多个线程可能在同一时间访问I/O设备或是修改内存中的同一个数据。
中断和异常
中断——进程外部使进程暂停的因素;
异常——进程内部的因素;
中断和异常处理程序——用于处理进程运行时发生的中断和异常,避免程序崩溃。一般流程如下:
1.保存当前程序运行的环境(PC、各种寄存器的值等);
2.加载中断和异常处理程序运行所需的环境;
3.运行处理程序;
4.加载原程序运行的环境;
5.继续运行当前程序。
OS中的多进程
类似于中断和异常的处理机制一样,多进程时分复用,使用计算机中的内存和计算资源。各个进程在运行的时候都会被设置一个计时器,当进程运行计时器所设置的时间后,程序被中断,保存完当前环境后再运行其他进程,周而复始。
虚拟内存
为什么会有虚拟内存?将多个进程所使用的物理内存区域分隔开来,避免进程修改不属于自己区域内的数据。
Lec 19 Virtual Memory
计算机中的内存一般使用的是DRAM(断电后数据将不再存在,对应于电脑关机后所有运行的进程都将被销毁)。
虚拟内存机制的作用:
1.隔绝各个进程访问的内存区域,保护数据;
2.给每个进程提供完全拥有所有内存的效果(通过外部存储单元,如固态等实现该效果);
虚拟内存通过”page memory“,即所谓的页面内存管理来实现,这里的一个page类似于缓存中的一个数据块。
操作系统通过进程提供的虚拟地址在page table中查找对应的物理地址,并执行相应的操作:
Lec 20 IO
CPU处理IO设备的方式,轮询(polling):
1.为IO设备在内存中分配特殊的地址空间,被称为控制寄存器和数据寄存器。控制寄存器用来表明该IO设备是否做好了接收/输入数据的准备;数据寄存器用来存储由IO输入/准备输出到IO的数据;
2.处理IO设备数据的进程时刻处于循环中,读取当前设备的控制寄存器,看其是否有效;
3.如果控制寄存器有效,则将数据存入数据寄存器/读取其中的数据;
轮询(polling)会占用CPU大量资源,一种更好的方法是中断,就和门铃一样,主人(CPU)不用定期到门口(控制寄存器)看是否会有客人(I/O)到来。当I/O处于活跃状态时,只需要中断当前程序,处理I/O相关数据即可。
对于需要处理大量数据的I/O设备,比如硬盘等,通常使用DMA(Direct Memory Access)机制进行访问:
1.CPU被中断,启动DMA;
2.CPU继续执行其他进程,DMA进行数据传输任务;
3.DMA执行完毕,将CPU中断。
Disc10 OS & Virtual Memory
虚拟内存存在的好处:
1.给每个进程无限内存的错觉;
2.隔绝每个进程访问的空间,避免可能出现的错误。
此外,VM中所使用的page table(页表)被存储在内存中,进程实际使用时要访问两次内存,第一次读取page table中的物理地址,第二次根据物理地址访问内存,这样时间消耗太大。因此有了TLB页表缓存的出现,将本进程常用的页表存储在TLB中,减少了CPU第一次访问的时间。
Lab 8: OS (context switch), I/O, DMA, Disks, Networking & Virtual Memory
使用lab中提供的camera仿真软件对VM中的TLB、page table进行仿真,建立起对VM机制直观的印象。以lw为例,总结一下整个过程:
1.初始化后TLB和page table都是空的;
2.获取当前进程的虚拟地址,然后查找TLB,为空;
3.根据虚拟地址查找内存中存储的page table,如果有效位为1,则说明内存中存在对应的page,直接转换为物理地址,CPU根据物理地址在内存中读取对应的值;
4.如果page table中对应位置有效位为0,即内存中不存在对应的page,则将外部存储设备(固态等)中的数据存入内存中的对应page(如果内存已满,则一般要替换掉最不常用的page,腾出孔空间),更新page table和TLB。
Link直接在lab08中下载对应软件即可,对虚拟内存和页表将理解得更加深刻。
Disc11 I/O
I/O,轮询、中断和DMA复习。
Lab 10: Thread Level Parallelism
多线程、进程编程基础,多线程竞争条件等。
The Rest
剩下的几节不是很感兴趣,略过。
课程总结
简短的总结:计算机架构
从软件硬件两个方面系统的介绍了计算机架构。软件部分以C语言为例,包括它的内存管理,stack,heap等。再是RISC-V指令集,详细介绍了软件从汇编编译再到CPU执行指令的过程;
硬件部分先从底层的晶体管开始,一路往上到常见的逻辑门,再介绍了CPU的基本架构,如算术逻辑单元ALU,控制单元,流水线操作等;
最后介绍了计算机体系中一些比较伟大的理念,如缓存,虚拟内存,进程和线程等。