找回密码
 立即注册
首页 业界区 业界 一生一芯学习:pa2.1 RTFM

一生一芯学习:pa2.1 RTFM

胆饬 2025-10-1 17:16:36
前面的内容后面再补,因为近期要C考核了,所以先准备下C考核所需的内容。
RTFSC(2)
整理一条指令在NEMU中的执行结果
从函数中跳转,宏嵌套中慢慢进入最终的代码,人肉gdb一下,
比如在sdb中执行一次si,那么根据cmd_si代码那我们会跳转到cpu_exec()中
  1. static int cmd_si(char *args) {
  2.   if (args == NULL) {
  3.     cpu_exec(1);
  4.     return 0;
  5.   }
  6.   int i = atoi(args);
  7.   if (i <= 0) {
  8.     printf("Invalid argument '%s'\n", args);
  9.   } else {
  10.     cpu_exec(i);
  11.   }
  12.   return 0;
  13. }
复制代码
从exec_once()中跳转到isa_exec_once()中。
  1. /* Simulate how the CPU works. */
  2. void cpu_exec(uint64_t n) {
  3.   g_print_step = (n < MAX_INST_TO_PRINT);//一次执行太多步就不打印了,bool类型的gprintstep就赋值为false
  4.   //printf("%d\n",nemu_state.state);
  5.   switch (nemu_state.state) {
  6.     case NEMU_END: case NEMU_ABORT: case NEMU_QUIT:
  7.       printf("Program execution has ended. To restart the program, exit NEMU and run again.\n");
  8.       return;//如果状态是结束了,出错了,退出了就打印“退出nemu”。
  9.     default: nemu_state.state = NEMU_RUNNING;//默认running
  10.   }
  11.   uint64_t timer_start = get_time();//获取执行指令前的时间
  12.   execute(n);
  13.   uint64_t timer_end = get_time();//获取执行指令后的时间
  14.   g_timer += timer_end - timer_start; //看执行了多久。
  15.   switch (nemu_state.state) {
  16.     case NEMU_RUNNING: nemu_state.state = NEMU_STOP; break;
  17.     case NEMU_END: case NEMU_ABORT:
  18.       Log("nemu: %s at pc = " FMT_WORD,
  19.         //nemu出错或者异常退出就用红色打印,正常退出就绿色打印。  
  20.         (nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) :
  21.            (nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) :
  22.             ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))),
  23.           nemu_state.halt_pc);
  24.       // fall through
  25.     case NEMU_QUIT: statistic();
  26.   }
  27. }
复制代码
从isa_exec_once中在跳转到decode_exec()函数中
  1. static void execute(uint64_t n) {
  2.   Decode s;
  3.   initBuffer(&cb); // 初始化环形缓冲区,大小为BUFFER_SIZE
  4.   for (;n > 0; n --) {
  5.     exec_once(&s, cpu.pc);
  6.     g_nr_guest_inst ++;
  7.     trace_and_difftest(&s, cpu.pc);
  8.     if (nemu_state.state != NEMU_RUNNING) {break;}
  9.     IFDEF(CONFIG_DEVICE, device_update());
  10.   }/*条件编译宏,如果CONFIG_DEVICE被定义,则调用device_update函数,如果 CONFIG_DEVICE 没有被定义,
  11.   这一行什么都不会生成(等价于被注释掉)。*/
  12.   printBuffer(&cb);
  13. }
复制代码
inst_fetch()是在snpc中获取一条长度为4字节的指令赋值给s->inst,然后执行一遍decode_exec()并返回decode_exec()的返回值。
先一次性将decode_exec()代码全部贴出,很长
  1. static void exec_once(Decode *s, vaddr_t pc) {
  2.   s->pc = pc;//当前指令地址
  3.   s->snpc = pc;//静态下一条指令地址,默认为pc+4
  4.   // if(s->pc == 0x80001480){
  5.   //   printf("找到一场入口地址\n");
  6.   //   nemu_state.state = NEMU_STOP;
  7.   // }
  8.   isa_exec_once(s);
  9.   cpu.pc = s->dnpc;//动态下一条指令,可能跳转或者分支改变
  10. #ifdef CONFIG_ITRACE//如果启用了 CONFIG_ITRACE,会记录指令的详细信息到日志缓冲区 s->logbuf:
  11.   char *p = s->logbuf;
  12.   
  13.   p += snprintf(p, sizeof(s->logbuf), FMT_WORD ":", s->pc);//FMT_WORD:格式化字符串(如 "0x%08x"),用于输出 PC 地址。
  14.   //printf("0x%08x\n",s->pc);
  15.   //printf("0x%08x\n",s->snpc);
  16.   int ilen = s->snpc - s->pc; //计算指令长度
  17.   int i;
  18.   uint8_t *inst = (uint8_t *)&s->isa.inst;
  19.   // printf("inst = 0x%08x\n",s->isa.inst);
  20.   // printf("inst ***= 0x%08x\n", *inst);
  21. #ifdef CONFIG_ISA_x86
  22.   for (i = 0; i < ilen; i ++) { //x86是小段,从低地址开始打印
  23. #else
  24.   for (i = ilen - 1; i >= 0; i --) {//riscv是大段,从高地址开始打印
  25. #endif
  26.     p += snprintf(p, 4, " %02x", inst[i]); //把指令打印出来
  27.   }
  28.   int ilen_max = MUXDEF(CONFIG_ISA_x86, 8, 4); //不是x86ilenmax就是4
  29.   int space_len = ilen_max - ilen;   //计算需要填充的空格数
  30.   if (space_len < 0) space_len = 0; //
  31.   space_len = space_len * 3 + 1;
  32.   memset(p, ' ', space_len);
  33.   p += space_len;
  34.   void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte);//反汇编指令
  35.   disassemble(p, s->logbuf + sizeof(s->logbuf) - p,   //将反汇编指令出来后传到logbuf里面
  36.       MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), (uint8_t *)&s->isa.inst, ilen);
  37.             //muxdef,有点像  ?:,
  38.   enqueue(&cb, s->logbuf);
  39. #endif
  40. }
复制代码
其中INSTPAT()意思是
首先我们来解析一下里面的宏函数

  • 先来看一下这个INSTPAT_MATCH()定义了一条模式匹配规则
INSTPAT(模式字符串, 指令名称, 指令类型, 指令执行操作);如果匹配上了,那就直接用C语言的goto跳转到INSTPAT_END表示为
  1. int isa_exec_once(Decode *s) {
  2.   s->isa.inst = inst_fetch(&s->snpc, 4);
  3.   //return一个0回去,但是现在并没有使用这个返回值,可以忽略他。
  4.   return decode_exec(s);
  5. }
复制代码
其中的s是译码所需的信息比如pc,snpc,dnpc,isa,logbuf(用于ITRACE)
其中的decode_operand
  1. static int decode_exec(Decode *s) {
  2.   int rd = 0;
  3.   word_t src1 = 0, src2 = 0, imm = 0;
  4.   s->dnpc = s->snpc;
  5. #define INSTPAT_INST(s) ((s)->isa.inst)
  6. #define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \
  7.   decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \
  8.   __VA_ARGS__ ; \
  9. }
  10.   INSTPAT_START();
  11.   //INSTPAT(模式字符串, 指令名称, 指令类型, 指令执行操作);
  12.   INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc  , U, R(rd) = s->pc + imm);
  13.   INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui    , U, R(rd) = imm);     
  14.   INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli   , I, R(rd) = src1 >> BITS(imm, 5, 0));
  15.   INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli   , I, R(rd) = src1 << BITS(imm, 5, 0));
  16.   INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai   , I, R(rd) = (int32_t)src1 >> BITS(imm , 4 , 0) );
  17.   INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu    , I, R(rd) = Mr(src1 + imm, 1));
  18.   INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi   , I, R(rd) = src1 + imm);
  19.   INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu  , I, R(rd) = (src1 < imm) ? 1 : 0);
  20.   INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti   , I, R(rd) = ((int32_t)src1 < ((int32_t)imm)) ? 1 : 0);
  21.   INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb     , I, R(rd) = SEXT(Mr(src1 + imm, 2),8));
  22.   INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh     , I, R(rd) = SEXT(Mr(src1 + imm, 2),16));
  23.   INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu    , I, R(rd) = Mr(src1 + imm, 2));
  24.   INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw     , I, R(rd) = Mr(src1 + imm, 4));
  25.   INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi   , I, R(rd) = src1 & imm);
  26.   INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori   , I, R(rd) = src1 ^ imm);
  27.   INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori    , I, R(rd) = src1 | imm);
  28.   //CSR寄存器
  29.   INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw  , I,  
  30.   if(imm == 0x305){  //mtvec
  31.     R(rd) = cpu.mtvec;
  32.     cpu.mtvec =  src1;
  33.   };
  34.   if(imm == 0x300){ //mstatus
  35.     R(rd) = cpu.mstatus;
  36.     cpu.mstatus =  src1;
  37.   };
  38.   if(imm == 0x341){ //mepc
  39.     R(rd) = cpu.mepc;
  40.     cpu.mepc =  src1;
  41.   };
  42.   if(imm == 0x342){ //mcause
  43.     R(rd) = cpu.mcause;
  44.     cpu.mcause =  src1;
  45.   };
  46. );
  47.   INSTPAT("0000000 00000 00000 000 00000 11100 11", ecall  , I, s->dnpc = isa_raise_intr(11,s->pc);etrace());
  48.   INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs  , I,
  49.   if(imm == 0x305){  //mtvec
  50.     R(rd) = cpu.mtvec;
  51.     cpu.mtvec |=  src1;
  52.   };
  53.   if(imm == 0x300){ //mstatus
  54.     R(rd) = cpu.mstatus;
  55.     cpu.mstatus |=  src1;
  56.   };
  57.   if(imm == 0x341){ //mepc
  58.     R(rd) = cpu.mepc;
  59.     cpu.mepc |=  src1;
  60.   };
  61.   if(imm == 0x342){ //mcause
  62.         // printf("??????????????????????????*****\n");
  63.     R(rd) = cpu.mcause;
  64.     cpu.mcause |=  src1;
  65.   };
  66. );
  67.   INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw     , S, Mw(src1 + imm, 4, src2));
  68.   INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh     , S, Mw(src1 + imm, 2, src2));
  69.   INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb     , S, Mw(src1 + imm, 1, src2));
  70.   INSTPAT("??????? ????? ????? 011 ????? 01000 11", sd     , S, Mw(src1 + imm, 8, src2));
  71.   INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal    , J, R(rd) = s->pc + 4;
  72.    s->dnpc = s->pc + imm;
  73.    IFDEF(CONFIG_FTRACE, {
  74.     if (rd == 1) {
  75.         call_trace(s->pc, s->dnpc);
  76.     }})
  77.    );
  78.   INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr   , I, R(rd) = s->pc + 4;
  79.    s->dnpc = (src1 + imm) & (~1);
  80.    IFDEF(CONFIG_FTRACE,{
  81.     if (s->isa.inst == 0x00008067)
  82.         ret_trace(s->pc);
  83.     else if (rd == 1) {call_trace(s->pc, s->dnpc);}
  84.     else if (rd == 0 && imm == 0) {call_trace(s->pc, s->dnpc);}
  85.    })
  86.    );
  87.   INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl    , R, R(rd) = src1 >> BITS(src2, 4, 0));
  88.   INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add    , R, R(rd) = src1 + src2);
  89.   INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll    , R, R(rd) = src1 <<  BITS(src2 , 4 , 0));
  90.   INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt    , R, R(rd) = ((int32_t)src1 < (int32_t)src2) ? 1 : 0);
  91.   INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu   , R, R(rd) = src1 < src2 ? 1 : 0);
  92.   INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor    , R, R(rd) = src1 ^ src2);
  93.   INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or     , R, R(rd) = src1 | src2);
  94.   INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and    , R, R(rd) = src1 & src2);
  95.   INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra    , R, R(rd) = (int32_t)src1 >> BITS(src2 , 4 , 0));
  96.   INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub    , R, R(rd) = src1 - src2);
  97.   INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul    , R, R(rd) = (unsigned)src1 * (unsigned)src2);
  98.   //INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div    , R, R(rd) = src1 / src2);
  99.   //INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem    , R, R(rd) = src1 % src2);
  100.   //INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu   , R, R(rd) = (unsigned)src1 % (unsigned)src2);
  101.   //INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu   , R, R(rd) = (unsigned)src1 / (unsigned)src2);
  102.   INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh   , R, R(rd) = ((int64_t)(int32_t)src1 * (int64_t)(int32_t)src2) >> 32;);
  103.   INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu , R, R(rd) = ((int64_t)(int32_t)src1 * (int64_t)(uint32_t)src2) >> 32;);
  104.   INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu  , R, R(rd) = ((int64_t)(uint32_t)src1 * (int64_t)(uint32_t)src2) >> 32;);
  105.   INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div    , R, if (src2 == 0) R(rd) = -1;else if ((int32_t)src1 == INT32_MIN && (int32_t)src2 == -1) R(rd) = INT32_MIN;else R(rd) = (int32_t)src1 / (int32_t)src2;);
  106.   INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu   , R, if (src2 == 0) R(rd) = 0xFFFFFFFF;else R(rd) = (uint32_t)src1 / (uint32_t)src2;);
  107.   INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem    , R, if (src2 == 0) R(rd) = (int32_t)src1;else if ((int32_t)src1 == INT32_MIN && (int32_t)src2 == -1) R(rd) = 0;else R(rd) = (int32_t)src1 % (int32_t)src2;);
  108.   INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu   , R, if (src2 == 0) R(rd) = (uint32_t)src1;else R(rd) = (uint32_t)src1 % (uint32_t)src2;);
  109.   INSTPAT("0011000 00010 00000 000 0000 011100 11", mret   , R, s->dnpc = cpu.mepc);
  110.   //div注释:
  111.   //匹配 div 指令(有符号除法)。
  112.   //如果除数 src2 为 0,结果规定为 -1。
  113.   //如果被除数是最小负数(INT32_MIN),除数为 -1,结果规定为 INT32_MIN(防止溢出)。
  114.   //否则正常做有符号除法。
  115.   
  116.   //printf("mulh:%lx\n", (int64_t)(~src1+1) * (int64_t)src2));
  117.   //正确的a5:0001 1001 1101 0010 1001 1010 1011 1001
  118.   //INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh   , R, R(rd) = SEXT(src1 * src2, 32));
  119.   //把寄存器 x[rs2]乘到寄存器 x[rs1]上,都视为 2 的补码,将乘积的高位写入 x[rd]。
  120.   
  121.   INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq    , B,
  122.     // if(s->pc == 0x800115c0){
  123.     // printf("src1 =%d\n",src1);
  124.     // printf("src2 =%d\n",src2);
  125.     // printf("pc =0x%08x\n",s->pc);
  126.     // printf("imm =0x%08x\n",imm);
  127.     // printf("dnpc =0x%08x\n",s->dnpc);  
  128.     // }
  129.     // printf("src1 =%d\n",src1);printf("src2 =%d\n",src2);
  130.     // printf("pc =0x%08x\n",s->pc);printf("imm =0x%08x\n",imm);
  131.     // printf("dnpc =0x%08x\n",s->dnpc);
  132.     if(src1 == src2) s->dnpc = s->pc + imm);
  133.   INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne    , B, if(src1 != src2) s->dnpc = s->pc + imm);
  134.   INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt    , B, s->dnpc = ((int32_t)src1< (int32_t)src2) ? s->pc + imm : s->dnpc);
  135.   INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge    , B, s->dnpc = ((int32_t)src1>=(int32_t)src2) ? s->pc + imm : s->dnpc);
  136.   INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu   , B, s->dnpc = (src1< src2) ? s->pc + imm : s->dnpc);
  137.   INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu   , B, s->dnpc = (src1>=src2) ? s->pc + imm : s->dnpc);
  138.         INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0
  139.   INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv    , N, INV(s->pc));
  140.         INSTPAT_END();
  141.   R(0) = 0; // reset $zero to 0
  142.   return 0;
  143. }
复制代码
理解为译码的预处理,BITS()的用法有点像verilog中的变量[1:0]的味道,也就是取这个区间的位宽,在看riscv手册中提到,指令中的源寄存器,目的寄存器,立即数基本上都有对应的位置。如果检测到一条指令是什么类型的便把需要的值赋给rs1,rs2,*rd,imm即可。此外SEXT()用于符号扩展。在匹配过程最后有一条inv的规则,如果前面所有的匹配模式都没有成功那就把此指令视为非法指令
此时取指、译码功能便实现了,接下来就是执行,访存、回写。
INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc  , U, R(rd) = s->pc + imm);
都在后面的R(rd) = s->pc + imm实现了。
这个操作懂了之后,后面运行更多的程序你就知道怎么做了。
首先程序会被riscv工具链交叉编译之后生成机器码之后将一串数据放到img中,你的nemu就根据img的值不断取指、译码、执行、访存、回写等等。那你此时只需要去看am的cpu-test被汇编成什么指令,比如展示一下dummy.c汇编出来的东西放在了dummy-riscv32-nemu.txt,你就只需看你少了什么指令然后补充上去就可以了。
下一期介绍一下cpu-test一堆例程是如何跑在nemu上运行的,顺便复习一下对AM的理解。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
昨天 00:42

举报

感谢,下载保存了
您需要登录后才可以回帖 登录 | 立即注册