调试RISC_CPU

我们上一篇文章讲述了RISC_CPU的结构,但是要验证RISC_CPU能否正确工作,还需要一些外围电路来提供ROM(测试程序),RAM(装载数据)以及地址译码器。下面我们将一一做介绍。

1、RISC_CPU寻址方式和指令系统

RISC_CPU的一条指令有16位,其中前3位是指令opcode,后13位是地址ir_addr。


指令.png

操作码opcode有8种操作,由着8中操作可以实现任意的运算。

  • HLT,停机操作。
  • SKZ,如果累加器的输出accum为0,则跳过下一条语句,否则继续执行。
  • ADD,将累加器中的值与地址所指的ram中的数据相加,结果仍然送回累加器。
  • AND,将累加器中的值与地址所指的ram中的数据相与,结果仍然送回累加器。
  • XOR,将累加器中的值与地址所指的ram中的数据相异或,结果仍然送回累加器。
  • LDA,将指令中给出的地址的数据放入累加器。
  • STO,将累加器上的数据放入指令中给出的地址。
  • JMP,无条件跳转语句,跳转指令给出的目的地址,继续执行。

RISC_CPU,是8位微处理器,一律采用直接寻址的方式,即数据总放在存储器中,寻址单元由指令直接给出。这是最简单的寻址方式。

2、外围电路

对RISC_CPU进行调试的话,需要外围电路提供ROM(测试程序),RAM(装载数据)以及地址译码器。

地址译码器用于产生选通信号,选通ROM或RAM。

module addr_decode (
                addr,
                ram_sel,
                rom_sel
                );

   input[12:0] addr;
   output ram_sel,rom_sel;
   reg rom_sel,ram_sel;

 always@(addr)
 begin
  casex(addr)
    13'b1_1xxx_xxxx_xxxx:{rom_sel,ram_sel}=2'b01;
    13'b0_xxxx_xxxx_xxxx:{rom_sel,ram_sel}=2'b10;
   13'b1_0xxx_xxxx_xxxx:{rom_sel,ram_sel}=2'b10;
   default:{rom_sel,ram_sel}=2'b00;
  endcase
end

endmodule

13'h1FFF-----13'h1800 RAM
13'h17FF-----13'h0000 ROM

module ram_cpu (
            addr,
              rd,
              wr,
              ena,
              data    
              );
input[12:0] addr;
input rd,wr,ena;
inout[7:0] data;
reg[7:0] memory_ram [13'h1fff:13'h1800];

 assign data=(rd&&ena)?memory_ram[addr]:8'bzzzzzzzz;

  always@(posedge wr)
  begin
   memory_ram[addr]<=data;
  end

endmodule

RAM用于存放的数据,可读可写。

 module rom_cpu (
            addr,
            rd,
            ena,
            data
            );
            
      input[12:0] addr;
      input rd,ena;
      output[7:0] data;
      wire[7:0] data;
     reg[7:0] memory_rom [13'h17ff:13'h0000]; 

     assign data=(ena&&rd)?memory_rom[addr]:8'bzzzzzzzz;

     endmodule               

ROM用于装载测试程序。

3、连接外围电路测试RSIC_CPU

调试RISC_CPU_第1张图片
cpu_top.png
    /*`include "cpu.v"
    `include "ram_cpu"
    `include "rom_cpu"
     `include "addr_decode"*/
     `timescale 1ns/1ns
    //`define PERIOD 100

      module cpu_top;
           reg rst,clk;                                                         
           reg[(3*8):0] mnemonic;
           reg[12:0] PC_addr,IR_addr;
           wire[7:0] data;
           wire HALT,rd,wr,ram_sel,rom_sel;
           wire fetch;
           wire[12:0] addr;
           integer test;
           // wire[2:0] opcode;
           // wire[12:0]ir_addr,pc_addr;

         cpu cpu_t(  .clk(clk),
          .rst(rst),
          .data(data),
          .rd(rd),
          .wr(wr),
          .addr(addr),
          .HALT(HALT),
          .fetch(fetch));

            ram_cpu ram_cpu_t(.addr(addr),
                .rd(rd),
                .wr(wr),
                .ena(ram_sel),
                .data(data));  
                
            rom_cpu rom_cpu_t(.addr(addr),
                .rd(rd),
                .ena(rom_sel),
                .data(data));
                
            addr_decode addr_decode_t(.addr(addr),
                        .ram_sel(ram_sel),
                        .rom_sel(rom_sel));     

            initial 
               begin
               clk=0;
                $timeformat (-9,1,"ns",12);
               display_debug_message;
               asyn_rst;
                //test1;
               //test2;
               //test3;
                test4;
              $finish;
              end          

      task display_debug_message;
        begin
           $display("\n*********************");
           $display("load test");
           $display("***********************\n");
        end
       endtask  

   task asyn_rst;
           begin
             rst=1;
             #10 
             rst=0;
             #40
             rst=1;
            end 
         endtask                                                        

       /*task test1;
begin
  test=0;
  disable MONITOR;
  $readmemb("C:/Users/XQ/Desktop/test_pro.txt",rom_cpu_t.memory_rom);
  $display("rom loaded successfully!");
  $readmemb("C:/Users/XQ/Desktop/test_dat.txt",ram_cpu_t.memory_ram);
  $display("ram loaded successfully!");
  #1 test=1;
  #10000;
  asyn_rst;
end
endtask
 
 always@(test)
   begin:MONITOR
$display("\n***running test1***");
$display("\n TIME PC INSTRUCTION ADDR DATA");
$display("  -------   ------   -----------   ------   ------");
while (test==1)
@(cpu_t.pc_addr)
if((cpu_t.pc_addr%2==1)&&(cpu_t.fetch==1))
  begin
    #30 PC_addr<=cpu_t.pc_addr-1;
        IR_addr<=cpu_t.ir_addr;
    #130 $strobe("%t %h %s %h %h",$time,PC_addr,mnemonic,IR_addr,data);
  end
end*/


  /*task test2;
begin
  test=0;
  disable MONITOR;
  $readmemb("C:/Users/XQ/Desktop/test_pro.txt",rom_cpu_t.memory_rom);
  $display("rom loaded successfully!");
  $readmemb("C:/Users/XQ/Desktop/test_dat.txt",ram_cpu_t.memory_ram);
  $display("ram loaded successfully!");
  #1 test=1;
  #10000;
  asyn_rst;
end
endtask

 always@(test)
begin:MONITOR
$display("\n***running test2***");
$display("\n  TIME      PC     INSTRUCTION   ADDR     DATA");
$display("  -------   ------   -----------   ------   ------");
while (test==1)
@(cpu_t.pc_addr)
if((cpu_t.pc_addr%2==1)&&(cpu_t.fetch==1))
  begin
    #30 PC_addr<=cpu_t.pc_addr-1;
        IR_addr<=cpu_t.ir_addr;
    #130 $strobe("%t %h %s %h %h",$time,PC_addr,mnemonic,IR_addr,data);
  end
 end*/

/*task test3;
begin
  test=0;
  disable MONITOR;
  $readmemb("C:/Users/XQ/Desktop/test_pro.txt",rom_cpu_t.memory_rom);
  $display("rom loaded successfully!");
  $readmemb("C:/Users/XQ/Desktop/test_dat.txt",ram_cpu_t.memory_ram);
  $display("ram loaded successfully!");
  #1 test=1;
  #1000000;
  asyn_rst;
end
 endtask

 always@(test)
 begin:MONITOR
$display("\n***running test3***");
$display("\n  TIME      FN1      FN2    TEMP");
$display("  -------    ------   ------  ----- ");
while (test==1)
 begin
   wait (cpu_t.opcode==3'h1)
   $strobe("%t       %d      %d    %d",$time,ram_cpu_t.memory_ram[13'h1800],ram_cpu_t.memory_ram[13'h1801],ram_cpu_t.memory_ram[13'h1802]);
   wait (cpu_t.opcode!=3'h1);
 end
end*/

 task test4;
  begin
  test=0;
  disable MONITOR;
  $readmemb("C:/Users/XQ/Desktop/test_pro.txt",rom_cpu_t.memory_rom);
  $display("rom loaded successfully!");
  $readmemb("C:/Users/XQ/Desktop/test_dat.txt",ram_cpu_t.memory_ram);
  $display("ram loaded successfully!");
  #1 test=1;
  #100000;
  asyn_rst;
end
 endtask

 always@(test)
 begin:MONITOR
$display("\n***running test4***");
$display("\n  TIME      FN1      FN2    FN3    TEMP");
$display("  -------    -------  ------- -----  ------");
while (test==1)
 begin
   wait (cpu_t.opcode==3'h1)
   $strobe("%t       %d      %d    %d    %d",$time,ram_cpu_t.memory_ram[13'h1800],ram_cpu_t.memory_ram[13'h1801],ram_cpu_t.memory_ram[13'h1802],ram_cpu_t.memory_ram[13'h1804]);
   wait (cpu_t.opcode!=3'h1);
 end
end

always@(posedge HALT)
begin
  #200
  $display("\n*********************");
  $display("**a HALT instruction was processed**");
  $display("*************************\n");
end

always #20 clk=~clk;

always@(cpu_t.opcode)
begin
  case(cpu_t.opcode)
      3'b000:mnemonic="HLT";
      3'b001:mnemonic="SKZ";
      3'b010:mnemonic="ADD";
      3'b011:mnemonic="AND";
      3'b100:mnemonic="XOR";
      3'b101:mnemonic="LDA";
      3'b110:mnemonic="STO";
      3'b111:mnemonic="JMP";
      default:mnemonic="???";
    endcase
end

endmodule

4、测试程序

在这里我简单列出几个测试程序供大家调试应用。

4.1 验证CPU逻辑功能的机器代码

该测试程序用来验证RISC_CPU的opcode的正确性。
ROM中的程序

 @0000
 111_00000 //00  begin:  JMP TST_JMP
 0011_1100 
 000_00000 //02          HLT  //JMP did not work at all
 0000_0000
 000_00000 //04          HLT //JMP did not load PC,it skipped
 0000_0000
 101_11000 //06 JMP_OK:  LDA DATA_1
 0000_0000
 001_00000 //08          SKZ
 0000_0000
 000_00000 //0a          HLT //SKZ or LDA did not work
 0000_0000
 101_11000 //0c          LDA DATA_2
 0000_0001
 001_00000 //0e          SKZ
 0000_0000
111_00000 //10          JMP SKZ_OK
 0001_0100
 000_00000 //12          HLT //SKZ or LDA did not work
 0000_0000
110_11000 //14 SKZ_OK:  STO TEMP //store non-zero vaule in TEMP
0000_0010
101_11000 //16          LDA DATA_1
 0000_0000
110_11000 //18          STO TEMP //store zero vaule in TEMP
 0000_0010
 101_11000 //1a          LDA TEMP
 0000_0010
 001_00000 //1c          SKZ //check to see if STO worked
 0000_0000
 000_00000 //1e          HLT //STO did not work
 0000_0000
 100_11000 //20          XOR DATA_2
 0000_0001
 001_00000 //22          SKZ //check to see XOR worked
 0000_0000
 111_00000 //24          JMP XOR_OK
 0010_1000
 000_00000 //26          HLT //XOR did not work at all
 0000_0000
 100_11000 //28 XOR_OK:  XOR DATA_2
 0000_0001
 001_00000 //2a          SKZ
0000_0000
000_00000 //2c          HLT //XOR did not switch all bits
0000_0000
000_00000 //2e END:     HLT //CONGRATULATIONS-TEST1 PASSED!
 0000_0000
 111_00000 //30          JMP BEGIN //run test again
 0000_0000

   @3c
   111_00000 //3c TST_JMP: JMP JMP_OK
   0000_0110
   000_00000 //3e          HLT //JMP is broken

RAM中的数据

 @1800
 00000000  //1800 DATA_1: //constant 00(hex)
 11111111  //1801 DATA_2: //constant FF(hex)
 10101010  //1802 TEMP:   //variable - starts with AA(hex)
4.2 验证CPU的高级指令集

ROM中存放的程序

    @0000
    101_11000 //00  begin: LDA DATA_2
    0000_0001  
    011_11000 //02         AND DATA_3
    0000_0010
    100_11000 //04         XOR DATA_2
     0000_0001
     001_00000 //06         SKZ
     0000_0000
     000_00000 //08         HLT         //AND doesn't work
     0000_0000
     010_11000 //0a         ADD DATA_1
     0000_0000
     001_00000 //0c         SKZ
     0000_0000
     111_00000 //0e         JMP          //ADD_OK
     0001_0010
     000_00000 //10         HLT          //ADD doesn't work
     0000_0000
     100_11000 //12  ADD_OK:XOR DATA_3
      0000_0010
     010_11000 //14         ADD DATA_1  //FE plus 1 makes FF
     0000_0000 
     110_11000 //16         STO TEMP
      0000_0011
     101_11000 //18         LDA DATA_1
     0000_0000
     010_11000 //1a         ADD TEMP   //FF plus 1 makes 100(hex)
     0000_0011
     001_00000 //1c         SKZ
     0000_0000
     000_00000 //1e         HLT        //ADD doesn't work
     0000_0000
     000_00000 //20  END:   HLT        //congratulations test passed
     0000_0000 
     111_00000 //22  JMP begin        //run test again
     0000_0000

RAM中存放的数据

  @1800
   00000001  //1800 DATA_1: //constant 01(hex)
   10101010  //1801 DATA_2: //constant AA(hex)
   11111111  //1802 DATA_3: //constant FF(hex)
   00000000  //1803 TEMP:   //variable - starts with 00(hex)
4.3 验证CPU的功能-Fibonacci序列

所谓Fibonacc序列,就是后一个数是前两个数相加的和,0、1、1、2、3、5、8、13、21、34......

调试RISC_CPU_第2张图片
Fibonacci.png

(1)将FN2放入TEMP
(2)将FN1+FN2的结果放入FN2
(3)将TEMP放入FN1
(4)将FN1与LIMIT做异或操作,决定循环是否继续进行

ROM中的指令

    @0000
     101_11000 //00  LOOP: LDA FN2  //load value in FN2 into accum
      0000_0001
      110_11000 //02        STO TEMP //store accum in TEMP
      0000_0010
      010_11000 //04        ADD FN1  
      0000_0000
      110_11000 //06        STO FN2
      0000_0001
      101_11000 //08        LDA TEMP 
      0000_0010
      110_11000 //0a        STO FN1
       0000_0000
       100_11000 //0c        XOR LIMIT
       0000_0011
       001_00000 //0e        SKZ
       0000_0000
       111_00000 //10        JMP LOOP
       0000_0000
      000_00000 //12  DONE: HLT
      0000_0000

RAM中的数据

    @1800
    00000001  //1800 FN1
    00000000  //1801 FN2
    00000000  //1802 TEMP
    10010000  //1803 LIMIT
4.4 验证CPU的功能-阶乘

此程序的功能就是计算5!


调试RISC_CPU_第3张图片
5的阶乘.png
调试RISC_CPU_第4张图片
Factorial.png

其中:

  • FN2用于计算累加的次数
  • FN3用于计算是否达到LIMIT
  • TEMP用于存放n!的结果

ROM中的程序

   @00
   101_11000  //00   LOOP1: LDA TEMP
   0000_0100
   010_11000  //02        ADD FN1
   0000_0000
   110_11000  //04        STO TEMP
    0000_0100
   101_11000  //06    LDA FN2
   0000_0001
   010_11000  //08    ADD DATA
   0000_0101
   110_11000  //0a    STO FN2
   0000_0001
   100_11000  //0c    XOR FN3
   0000_0010
   001_00000  //0e      SKZ
   0000_0000
   111_00000  //10   JMP LOOP1
   0000_0000
   101_11000  //12   LDA FN3
     0000_0010
     010_11000  //14     ADD DATA
     0000_0101
    110_11000  //16  STO FN3
    0000_0010
    101_11000  //18  LDA DATA
     0000_0101
    110_11000  //1a  STO FN2
     0000_0001
    101_11000  //1c  LDA TEMP
    0000_0100
    110_11000  //1e  STO FN1
    0000_0000
    101_11000  //20  LDA FN3
    0000_0010
    100_11000  //22  XOR LIMIT
    0000_0011
    001_00000  //24    SKZ
    0000_0000
    111_00000  //26  JMP LOOP1
    0000_0000
    000_00000  //28  DONE: HLT
    0000_0000

RAM中的数据

     @00
       0000_0001    //1800 FN1  
       0000_0001    //1801 FN2
       0000_0010    //1802 FN3
       0000_0110    //1803 LIMIT
       0000_0001    //1804 TEMP
       0000_0001    //1805 DATA

你可能感兴趣的:(调试RISC_CPU)