首页 > 其他分享 >pa2学习笔记

pa2学习笔记

时间:2024-10-20 12:17:43浏览次数:3  
标签:src pa2 map 笔记 学习 nemu 指令 内存 cpu

目录

硬编码与软编码

  1. 硬编码: 数据与程序都通过代码来编程
  2. 软编码:数据单独利用txt等文件来编写,程序单独通过代码实现,将数据与代码分开

YEMU

#include <stdint.h>
#include <stdio.h>

#define NREG 4    //reg的大小
#define NMEM 16   //mem的大小

// 定义指令格式
//union中每次只能存在三个数据中的一个数据
//三种数据长度都为8位,rtype表示reg的状态,mtype表示mem的状态,inst表示指令,即下面的0bxxxxxxxx
typedef union {
//struct的数据排布为反过来的:先op,再rt,最后rs
  struct { uint8_t rs : 2, rt : 2, op : 4; } rtype;   //:2表示位宽为2
  struct { uint8_t addr : 4      , op : 4; } mtype;
  uint8_t inst;
} inst_t;

//定义宏,用来提取union中的数据
#define DECODE_R(inst) uint8_t rt = (inst).rtype.rt, rs = (inst).rtype.rs
#define DECODE_M(inst) uint8_t addr = (inst).mtype.addr

uint8_t pc = 0;       // PC, C语言中没有4位的数据类型, 我们采用8位类型来表示
uint8_t R[NREG] = {}; // 寄存器
uint8_t M[NMEM] = {   // 内存, 其中包含一个计算z = x + y的程序

//0b表示二进制,后面的八位二进制则是指令,编写方法与pa2中提供的指令手册对应
  0b11100110,  // load  6#     | R[0] <- M[y]  第六个字节处是y的值
  0b00000100,  // mov   r1, r0 | R[1] <- R[0]
  0b11100101,  // load  5#     | R[0] <- M[x]  第五个字节处是x的值
  0b00010001,  // add   r0, r1 | R[0] <- R[0] + R[1]
  0b11110111,  // store 7#     | M[z] <- R[0]  将z值存在第七个字节处
  0b00010000,  // x = 16
  0b00100001,  // y = 33
  0b00000000,  // z = 0
  
};

int halt = 0; // 结束标志

// 执行一条指令
void exec_once() {
  inst_t this;
  this.inst = M[pc]; // 将mem中的指令读取到union中的inst中
  //将inst转化成rtype来解读,并译码
  switch (this.rtype.op) {
  //  操作码译码       操作数译码           执行
    case 0b0000: { DECODE_R(this); R[rt]   = R[rs];   break; }
    case 0b0001: { DECODE_R(this); R[rt]  += R[rs];   break; }
    case 0b1110: { DECODE_M(this); R[0]    = M[addr]; break; }
    case 0b1111: { DECODE_M(this); M[addr] = R[0];    break; }
    default:
      printf("Invalid instruction with opcode = %x, halting...\n", this.rtype.op);
      halt = 1;
      break;
  }
  pc ++; // 更新PC
}

int main() {
  while (1) {
    exec_once();
    if (halt) break;
  }
  printf("The result of 16 + 33 is %d\n", M[7]);
  return 0;
}

NEMU 执行一条指令的过程

(以执行auipc指令为例子)

  1. 取指令

    1. isa_exec_once(s)
      1. 取指令并更新pc指向的地址
      2. 利用所取指令译码(decode_exec)
      3. 返回译码后的值
  2. 译码decode_exec(s)

    1. 制定匹配规则:INSTPAT![[Pasted image 20240729165437.png]]

    2. 提取指令中用于匹配的信息:pattern_decode
      1. 提取key:提取模式字符串中的0、1,变成 key,不同的key代表不同的模式
      2. 产生mask:产生key的掩码mask
      3. 产生shift:表示opcode距离最低位的比特数量, 用于帮助编译器进行优化.
      4. 结果为:(以auipc指令为例)![[Pasted image 20240729205014.png]]

    3. 开始匹配:将(( 所取指令>>shift ) & mask ) == key 来匹配模式

    4. 提取指令中用于操作的信息:decode_operand:
      1. 提取寄存器号码
      2. 提取两个源操作数
      3. 提取立即数(直接在指令中可以使用的常数)

  3. 执行指令:按指令中的信息来执行(在INSTPAT_MATCH中)

    1. ![[Pasted image 20240729205649.png]]
  4. 更新pc(exec_once)

    1. 下一条静态指令snpc:在指令集中按顺序存放的下一条指令

    2. 下一条动态指令dnpc:程序运行将运行的下一条指令

    3. ![[Pasted image 20240729210754.png]]

    4. 要用dnpc来更新pc

ELF文件的组成

  • 需求:要做到代码和数据分离,并分别记录他们的信息

  • 解决:使用结构体(数据结构)

    • ![[Pasted image 20240811095957.png]]

    • 常见的节![[Pasted image 20240811100841.png]]

  • 结构![[Pasted image 20240811101806.png]]

ELF文件解析

用fopen打开文件

读取elf header的信息

elf header:显示elf文件总体的信息
![[Pasted image 20240806111608.png]]

Elf32_Ehdr 结构体 (在elf.h中定义)
![[Pasted image 20240806111759.png]]

和上面的header结构相对应,只需将数据读取进这个结构体中即可

利用fread,将文件中 elf header的数据,读入Elf32_Ehdr 结构体中(内部各个类型的数据会自动匹配)

解析elf header

现在结构体中存储了 elf header 的全部信息

  1. 检测魔数是否符合elf文件的规定 -> 检查读入的数据是不是elf文件
  2. 获取section headers 的起始位置
  3. 获取section headers 的数量
  4. 获取section headers 的大小

解析section headers

section headers: 列举了几个section header的信息
![[Pasted image 20240806112635.png]]

Elf32_Shdr 结构体,存放每一个section header的信息(每一行)
![[Pasted image 20240806112747.png]]

寻找字符串表:

  1. 用for循环遍历每一个section,并读入数据
  2. 从读入数据中查看type,看看是不是strtab
  3. 如果是,则获取 strtab 的 off 和 size
  4. 读取strtab的数据:将光标移动到 off 处 ,读取 size 大小的数据

寻找符号表:

  1. 用for循环遍历每一个section,并读入数据
  2. 从读入数据中查看type,看看是不是symtab
  3. 如果是,则获取 strtab 的 off ,并计算符号表中有多少个符号

解析符号表

symtab:记录了每个符号的信息
![[Pasted image 20240806113538.png]]

Elf32_Sym 结构体: 用于存储每个符号的信息
![[Pasted image 20240806113632.png]]

  1. 将光标移到符号表
  2. 遍历所有符号,读入每个符号的数据并存入结构体中
  3. 查看符号的类型是不是 func 函数
  4. 如果是:
    1. 存储函数名称
    2. 存储函数大小
    3. 存储函数地址

BIOS程序

全称:Basic Input/Output System

输入输出

cpu与设备的交互方式 ( 内存映射 )

  • cpu与设备约定好某一块内存作为两者交互的通道,两方通过往该内存地址写入或读出数据来进行交互
  • 在nemu中am中的test程序扮演运行在计算机上的程序(不是设备)
  • 设备(回调函数)
  • nemu主文件夹中程序扮演cpu的角色

(serial为例)

  1. cpu与设备约定
    • 在nemu/src/device/serial.c中的init_serial()中
  2. 程序的操作(实际是对内存操作,会被转化成对内存操作的指令)
    • 程序:am-kernels/tests/am-tests/src/tests/hello.c
    • 接口:abstract-machine/am/src/riscv/riscv.h
    • out(b/w/l):将数据输出给串口(将数据写入内存,让串口读取)
    • 程序的操作不会直接对cpu操作,而是转换成指令,再由cpu执行
  3. cpu对执行程序的操作
    • 程序被编译成对内存操作的指令,执行Mw的宏(其实是paddr_write)
    • paddr_write发现内存地址不是物理地址(那就是与设备约定好的内存地址,则调用map_write
    • 在nemu/src/device/io/map.c中的map_write
      • map_write
        1. 将数据写入内存后(cpu)
        2. 触发读出内存数据的回调函数(设备)
  4. 设备(回调函数)
    • nemu/src/device/serial.c
    • serial_io_handler将内存中的数据读出并通过串口输出

(RTC为例)

  1. cpu与设备约定
    • 在nemu/src/device/timer.c中的 init_timer() 中
  2. 程序的操作(实际是对内存操作,会被转化成对内存操作的指令)
    • 程序:am-kernels/tests/am-tests/src/tests/rtc.c
    • 接口:abstract-machine/am/src/platform/nemu/ioe/timer.c
    • in(b/w/l):读取时钟(设备)的数据(读取时钟写入内存的数据)
    • 程序的操作不会直接对cpu操作,而是转换成指令,再由cpu执行
  3. cpu对执行程序的操作
    • 程序被编译成对内存操作的指令,执行Mr的宏(其实是paddr_read
    • paddr_read发现内存地址不是物理地址(那就是与设备约定好的内存地址,则调用map_read
    • 在nemu/src/device/io/map.c中的map_read
      • map_read
      1. 先触发写入数据的回调函数(设备)
      2. 再读出内存的数据(cpu)
  4. 设备(回调函数)
    • nemu/src/device/timer.c
    • rtc_io_handler将时钟(设备)的数据写入内存中

键盘的数据传输过程

  1. cpu与设备约定
    • 在nemu/src/device/keyboard.c中的 init_i8042() 中
  2. 程序的操作(实际是对内存操作,会被转化成对内存操作的指令)
    • 程序:am-kernels/tests/am-tests/src/tests/keyboard.c
    • 接口:abstract-machine/am/src/platform/nemu/ioe/input.c
    • in(b/w/l):读取键盘(设备)的数据(读取键盘写入内存的数据)
    • 程序的操作不会直接对cpu操作,而是转换成指令,再由cpu执行
  3. cpu对执行程序的操作
    • 程序被编译成对内存操作的指令,执行Mr的宏(其实是paddr_read
    • paddr_read发现内存地址不是物理地址(那就是与设备约定好的内存地址,则调用map_read
    • 在nemu/src/device/io/map.c中的map_read
      • map_read
      1. 先触发写入数据的回调函数(设备)
      2. 再读出内存的数据(cpu)
  4. 设备(回调函数)
    • nemu/src/device/keyboard.c
    • i8042_data_io_handler 将键盘(设备)的数据写入内存中

键盘的 枚举 宏定义 展开过程

![[Pasted image 20240829150613.png]]

![[Pasted image 20240829150632.png]]

![[Pasted image 20240829150646.png]]![[Pasted image 20240829150656.png]]
在这里插入图片描述

如何检测多个键同时被按下?

  • 当按键被按下或松开,都有专门的指示信号keydown
  • 因此游戏让按键生效的判断条件可以是:
    • 当keydown=1时,按键生效
    • 当keydown=0时,按键失效
  • 这样可以让多个按键的keydown同时为1

VGA的数据传输过程(没有回调函数)

  1. 设备:初始化void init_vga()
    1. 存入屏幕大小的信息
    2. 将显存内容清零
  2. 程序
    1. abstract-machine/am/src/platform/nemu/ioe/gpu.c 和 am-kernels/tests/am-tests/src/tests/video.c
    2. 取出屏幕大小信息
    3. 存入图像信息
    4. 将同步信号存1
  3. cpu执行程序
    1. map read读出屏幕大小信息
    2. map write写入图像数据
    3. map write写入同步信号为1
  4. 设备:自己每隔一段时间更新状态 device_update()
    1. 调用 vga_update_screen 检查同步信号
    2. 若同步信号为1,则读取图像信息并显示(刷新屏幕)
    3. 刷新屏幕后会将同步信号置0
  5. 重复2.到5.

PA2.3必答题

编译与链接

  1. nemu/include/cpu/ifetch.h中, 你会看到由static inline开头定义的inst_fetch()函数. 分别尝试去掉static, 去掉inline或去掉两者, 然后重新进行编译, 你可能会看到发生错误. 请分别解释为什么这些错误会发生/不发生? 你有办法证明你的想法吗?

    • 因为函数定义在.h文件中,如果去掉static则可能会出现重定义的情况
    • 如果去掉inline,作为函数调用,需要占用更多的资源
  2. nemu/include/common.h中添加一行volatile static int dummy; 然后重新编译NEMU. 请问重新编译后的NEMU含有多少个dummy变量的实体? 你是如何得到这个结果的?

    • 有一个实体变量,因为static保证变量的作用域只在common.h中
  3. 添加上题中的代码后, 再在nemu/include/debug.h中添加一行volatile static int dummy; 然后重新编译NEMU. 请问此时的NEMU含有多少个dummy变量的实体? 与上题中dummy变量实体数目进行比较, 并解释本题的结果.

    • 含有两个实习,因为都是static,所以都在各自的作用域中
  4. 修改添加的代码, 为两处dummy变量进行初始化:volatile static int dummy = 0; 然后重新编译NEMU. 你发现了什么问题? 为什么之前没有出现这样的问题? (回答完本题后可以删除添加的代码.)

    • 为同名的变量初始化后会被视为重复定义,

标签:src,pa2,map,笔记,学习,nemu,指令,内存,cpu
From: https://blog.csdn.net/2201_75757246/article/details/143080755

相关文章

  • 20241313 刘鸣宇 《计算机基础与科学概论》第四周学习总结
    《C语言程序设计》学习总结1.学习了基础算数运算符并通过AI了解了常用运算符的优先级2.学习了复合的赋值运算符,增1与减1运算符,以及前缀与后缀的不同3.学习了宏常量与宏替换#define4.学习了const函数并对比了解const函数相比于宏常量的优势(const函数有数据类型,编译器能对其经......
  • 2024-2025-1 20241304 《计算机基础与程序设计》第4周学习总结
    2024-2025-120241304《计算机基础与程序设计》第4周学习总结作业信息|这个作业属于哪个课程|<2024-2025-1-计算机基础与程序设计)|>|-- |-- ||这个作业要求在哪里|<作业要求的链接>(https://www.cnblogs.com/rocedu/p/9577842.html#WEEK04))||这个作业的目标|<复习第四章内......
  • 神经网络与机器学习的区别及例子?CNN是有监督学习吗?
    一、神经网络和机器学习在概念上有所区别,但也紧密相关。以下是它们的主要区别以及一些例子:区别:定义:机器学习:是人工智能的一个分支,使计算机系统能够利用数据来不断改进性能,无需明确编程。它侧重于开发算法,让计算机通过经验学习。神经网络:是一种受人脑结构启发的机器学习算......
  • 2024-2025-1 20241327 《计算机基础与程序设计》第四周学习总结
    作业信息|2024-2025-1-计算机基础与程序设计)||--|-|2024-2025-1计算机基础与程序设计第四周作业)||快速浏览一遍教材计算机科学概论(第七版),课本每章提出至少一个自己不懂的或最想解决的问题并在期末回答这些问题|作业正文|https://www.cnblogs.com/shr060414/p/18440575|教......
  • 2024-2025学年 20241306学号 《计算机基础与程序设计》第四周学习总结
    学期2024-2025学号20241306《计算机基础与程序设计》第四周学习总结作业信息这个作业属于哪个课程2024-2025-1-计算机基础与程序设计(https://edu.cnblogs.com/campus/besti/2024-2025-1-CFAP))这个作业要求在哪里[2024-2025-1计算机基础与程序设计第一周作业(https:......
  • java计算机毕业设计在线学习系统设计与实现(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着信息技术的迅猛发展,互联网已经深入到社会的各个角落。在教育领域,传统的教学模式受到了前所未有的挑战与机遇。传统教学往往受限于时间和空间......
  • .NET数据挖掘与机器学习开源框架
     数据挖掘与机器学习开源框架1.1 框架概述1.1.1 AForge.NETAForge.NET是一个专门为开发者和研究者基于C#框架设计的,他包括计算机视觉与人工智能,图像处理,神经网络,遗传算法,机器学习,模糊系统,机器人控制等领域。这个框架由一系列的类库组成。主要包括有:AForge.Imaging——......
  • 系统架构设计师教程 第18章18.8 安全架构设计案例分析 笔记
    18.8安全架构设计案例分析18.8.1电子商务系统的安全性设计认证、授权和审计(AuthenticationAuthorizationandAccounting,AAA)是运行于宽带网络接入服务器上的客户端程序RADIUS软件主要应用于宽带业务运营的支撑管理,是一个需要可靠运行且高安全级别的软件支撑系......
  • 系统架构设计师教程 第18章 18.7 系统架构的脆弱性分析 笔记
    18.7系统架构的脆弱性分析18.7.1概述安全架构的设计核心是采用各种防御手段确保系统不被破坏,而系统的脆弱性分析是系统安全性的另一方面技术,即系统漏洞分析。漏洞的来源:1.软件设计时的瑕疵2.软件实现中的弱点3.软件本身的瑕疵4.系统和网络的错误配置18.7.2软件脆......
  • 从零开始学习VLSI设计(二)Yosys工具进阶
    系列文章目录从零开始学习VLSI设计(一)Yosys工具概述目录系列文章目录前言一、使用xdot,以图像形式查看电路逻辑二、使用Yosys进行逻辑优化1.opt指令逻辑优化2.share指令资源共享总结参考文献前言  今天更新Yosys的进阶操作,包括如何使用xdot查看电路图及对数字......