Verilog与数电[2]-加入极简RISC-V的IP核

保命声明:笔者在校属于中水平学生,代码能力有限,若行文中有错漏之处欢迎大家指出。

加入IP核

IP核分类

[https://zhuanlan.zhihu.com/p/395628860]

  • 软核:软核IP是指交付的为RTL代码以及对应的设计描述文档等(包括源码及完整文档)
  • 硬核:硬核是指经过验证之后的设计版图,就是在特定工艺下经过布局布线等优化处理,最终的出pin位置也已固定
  • 固核:固核IP是指的交付的是带有平面规划信息的网表,一般是指在特定工艺下完成了综合网表的转化

IP核下载

开源RISC-V的IP核心下载

[https://blog.csdn.net/weixin_42470069/article/details/107753570]
[https://blog.csdn.net/weixin_44956956/article/details/122300217]
[https://github.com/cliffordwolf/picorv32]

RISCV指令集卡片


RISC-V IP核厂商

SiFive


平头哥

其他常用IP核下载

[https://www.iteye.com/resource/hmilypeng-4333931]
包括:
3des,395vgs,ata,avr_core,camera,core_arm,i2c,jtag,memory_cores,
memory_sizer,memory_cores2,pci_core,sdram_ctrl,usb11,video_compression_system

IP核下载站(超极全)

[https://opencores.org]
Since 1999, OpenCores is the most prominent online community for the development of gateware IP (Intellectual Properties) Cores. It is the place where such cores are shared and promoted in the spirit of Free and Open Source collaboration.

用Vivado仿真一个极简RISC-V

[https://blog.csdn.net/Jefferymeng/article/details/124282429]

新建一个a_simple_riscv项目
main.v

`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 09:21:49
// Design Name: clkgenerator,时钟发生器
module clkgenerator(clk,rst,clk1,clk2,clk4,fetch,alu_clk);
input clk,rst;
output wire clk1;
output reg clk2,clk4,fetch,alu_clk;//三个不同时钟,fetch时钟,alu时钟,全部要是reg类型

reg [7:0] state;
parameter S1=8'b0000_0001,
          S2=8'b0000_0010,
          S3=8'b0000_0100,
          S4=8'b0000_1000,
          S5=8'b0001_0000,
          S6=8'b0010_0000,
          S7=8'b0100_0000,
          S8=8'b1000_0000,
          idle=8'b0000_0000;
//利用状态机来写,提高了代码的可综合性
assign clk1=~clk;

always @(negedge clk)
    if(rst)
        begin
            clk2<=0;
            clk4<=0;
            fetch<=0;
            alu_clk<=0;
            state<=idle;
        end
    else
        begin
            case(state)
                S1:
                    begin
                    clk2<=~clk2;//clk2每次都反向,其实是clk的二倍
                    alu_clk<=~alu_clk;
                    state<=S2;
                    end
                S2:
                    begin
                    clk2<=~clk2;
                    clk4<=~clk4;
                    alu_clk<=~alu_clk;//alu在一个大周期(8个系统周期)内只有一次
                    state<=S3;
                    end
                S3:
                    begin
                    clk2<=~clk2;
                    state<=S4;
                    end
                S4:
                    begin
                    clk2<=~clk2;
                    clk4<=~clk4;//clk4每两个系统时钟变一次,就是四倍的系统周期
                    fetch<=~fetch;//fetch每个大周期内变一次,是八个系统周期
                    state<=S5;
                    end
                S5:
                    begin
                    clk2<=~clk2;
                    state<=S6;
                    end
                S6:
                    begin
                    clk2<=~clk2;
                    clk4<=~clk4;
                    state<=S7;
                    end
                S7:
                    begin
                    clk2<=~clk2;
                    state<=S8;
                    end
                S8:
                    begin
                    clk2<=~clk2;
                    clk4<=~clk4;
                    fetch<=~fetch;
                    state<=S1;
                    end
                idle:
                    begin
                    state<=S1;//初始状态
                    end
                default://为了避免电路级错误而写default
                    begin
                    state<=idle;
                    end
            endcase
        end
endmodule
`timescale 1ns / 1ps
//
// Company: 微电子92
// Engineer: jeff
// Create Date: 2022/03/24 09:55:26
// Design Name: 
// Module Name: instr_reg
module instr_reg(opc_iraddr,data,ena,clk1,rst);
output [15:0] opc_iraddr;//这个就是输出的16进制opcode的地址
input [7:0] data;        //这个是输入数据
input ena,clk1,rst;      // load_ir输入进来,表示这时是否进行指令地址寄存
reg [15:0] opc_iraddr;

reg state;
always @(posedge clk1)
    begin
        if(rst)
            begin
            opc_iraddr<=16'b0000_0000_0000_0000;
            state<=1'b0;
            end
        else
            begin
                if(ena)
                    begin
                        casex(state)
                            1'b0:
                                begin
                                opc_iraddr[15:8]<=data;//先存高八位
                                state<=1;
                                end
                            1'b1:
                                begin
                                opc_iraddr[7:0]<=data;//再存低八位
                                state<=0;
                                end
                            default:
                                begin
                                opc_iraddr[15:0]<=16'bxxxx_xxxx_xxxx_xxxx;
                                state<=1'bx;
                                end
                        endcase
                    end
                else
                    state<=1'b0;//如果总线不是指令,则state一直是0
    end
    end
endmodule
`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 10:19:48
// Design Name: 累加器模块

module accumulator(accum,data,ena,clk1,rst);
output [7:0] accum;
input [7:0] data;
input ena,clk1,rst;
reg [7:0] accum;

always @(posedge clk1 or negedge rst)
    begin
        if(rst)
            accum<=8'b0000_0000;//进行reset
        else
            if(ena)
                accum<=data;//ena时,信号正常输出
    end
endmodule

`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 10:27:22
// Design Name: alu算数逻辑单元
module alu(alu_out,zero,data,accum,alu_clk,opcode);
output [7:0] alu_out;
output zero;
input [2:0] opcode;
input [7:0] data,accum;
input alu_clk;
reg [7:0] alu_out;

parameter HLT=3'b000,   //暂停指令,将操作数accum传输到输出
          SKZ=3'b001,   //跳过指令,也是将操作数传输到输出
          ADD=3'b010,    //加法
          ANDD=3'b011,   //位与运算
          XORR=3'b100,   //位异或运算
          LDA=3'b101,    //传输指令,将data传输到输出
          STO=3'b110,    //存储指令,将accum传输到输出
          JMP=3'b111;    //跳转指令,将accum传输到输出
assign zero=!accum;//accum如果是全0,那么就输出zero标识位为1;
always @(posedge alu_clk)
    begin
        casex(opcode)
            HLT: alu_out<=accum;
            SKZ: alu_out<=accum;
            ADD: alu_out<=data+accum;
            ANDD: alu_out<=data&accum;
            XORR: alu_out<=data^accum;
            LDA: alu_out<=data;
            STO: alu_out<=accum;
            JMP: alu_out<=accum;
            default: alu_out<=8'bxxxx_xxxx;
        endcase
    end
endmodule
`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 11:01:24
// Design Name: 数据控制器
module data_ctrl(data,in,data_ena);
output [7:0] data;
input [7:0] in;
input data_ena;

assign data=(data_ena)? in:8'bzzzz_zzzz;
//data_ena如果为0,输出高阻;
//data_ena如果为1,将输入进行输出;
//这相当于一个受控的buffer
endmodule
`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 11:07:11
// Design Name: 地址多路器
module addr(addr,fetch,ir_addr,pc_addr);
output [12:0] addr;
input [12:0] ir_addr,pc_addr;
input fetch;

assign addr=fetch? pc_addr:ir_addr;//在fetch的高电平期间读pc地址,低电平期间读数据或端口的地址
endmodule
`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 11:16:39
// Design Name: 程序计数器
module counter(pc_addr,ir_addr,load,clk,rst);
output [12:0] pc_addr;
input [12:0] ir_addr;
input load,clk,rst;
reg [12:0] pc_addr;

always @(posedge clk or posedge rst)
    begin
        if(rst)
            pc_addr<=13'b0000_0000_0000;
        else
            if(load)//load是1时,直接将输入进行传输
                pc_addr<=ir_addr;
            else
                pc_addr<=pc_addr+1;
    end
endmodule

`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 11:27:56
// Design Name: 状态机控制器
module state_ctrl(ena,fetch,rst);
output ena;
input fetch,rst;
reg ena;
always @(posedge fetch or posedge rst)
    begin
        if(rst)
            ena<=0;
        else
            ena<=1;
//state_ctrl模块的作用就是:在fetch上升沿时刻,如果rst是高,那就enable为低,否则就正常工作
    end
endmodule

`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 11:27:21
// Design Name: 状态机
module state_machine(inc_pc,load_acc,load_pc,rd,wr,load_ir,
                        datactl_ena,halt,clk1,zero,ena,opcode);
output inc_pc,load_acc,load_pc;//指示信号
output rd,wr;//指示是进行读还是写状态
output load_ir; //指示是否进行装载(寄存地址)
output datactl_ena,halt;//输出的是data_ctrl的使能,以及halt信号
input clk1,zero,ena;    //输入的零标志位,主控时钟是clk1(最快的那个),使能(由state_ctrl模块输出)
input [2:0] opcode;//输入的操作码

reg inc_pc,load_acc,load_pc,rd,wr,load_ir;
reg datactl_ena,halt;

parameter   HLT = 3 'b000, 
            SKZ = 3 'b001, 
            ADD = 3 'b010, 
            ANDD = 3 'b011, 
            XORR = 3 'b100, 
            LDA = 3 'b101, 
            STO = 3 'b110, 
            JMP = 3 'b111;
            
reg [2:0] state;
always @(negedge clk1)
    begin
        if(!ena)
            begin
            state<=3'b000;
            {inc_pc,load_acc,load_pc,rd}<=4'b0000;
            {wr,load_ir,datactl_ena,halt}<=4'b0000;//在enable信号为低时,进行全部置零的操作
            end
        else 
            ctl_cycle;//在enable为1的情况下,每次clk1的上升沿都进行一次task
    end
    //———————ctl_cycle task————————
task ctl_cycle;
    begin
        casex(state)//是一个state数量为8的状态机,一个完整的操作周期
        3'b000://进来后的第一个状态
            begin
                {inc_pc,load_acc,load_pc,rd}<=4'b0001;//第一次:rd和load_ir置高,寄存器读rom传过来的8位指令数据(高八位)
                {wr,load_ir,datactl_ena,halt}<=4'b0100;
                state<=3'b001;//按顺序进行下面的状态!
            end
        3'b001:
            begin//第二次:inc_pc和rd,load_ir置高,pc会加一,并且继续读rom的八位指令数据(低八位)
                {inc_pc,load_acc,load_pc,rd}<=4'b1001;
                {wr,load_ir,datactl_ena,halt}<=4'b0100;
                state<=3'b010;
            end
        3'b010:
            begin//第三次:空操作
                {inc_pc,load_acc,load_pc,rd}<=4'b0000;
                {wr,load_ir,datactl_ena,halt}<=4'b0000;
                state<=3'b011;
            end
        3'b011:
            begin//第四次:pc会加一,但前提是操作码不是HLT
                if(opcode==HLT)
                    begin//如果操作码是hlt,说明要暂停,这时输出一个hlt标志位,并且是pc加一
                    {inc_pc,load_acc,load_pc,rd}<=4'b1000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0001;
                    end
                else
                    begin//如果不是hlt,pc正常加一
                    {inc_pc,load_acc,load_pc,rd}<=4'b1000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                state<=3'b100;
            end
        3'b100:
            begin//第五次:对不同操作符进行分支赋值
                if(opcode==JMP)
                    begin//如果是jump,跳过这一条,那么就直接load_pc,把目的地址送给程序计数器
                    {inc_pc,load_acc,load_pc,rd}<=4'b0010;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                else if(opcode==ADD || opcode==ANDD ||
                        opcode==XORR|| opcode==LDA)
                      begin//如果是ADD,ANDD,XORR,LDA,那就正常进行read,计算得到数据
                      {inc_pc,load_acc,load_pc,rd}<=4'b0001;
                      {wr,load_ir,datactl_ena,halt}<=4'b0000;
                      end
                else if(opcode==STO)
                    begin//如果是STO,那就将datactl_ena(数据控制模块使能)置高,输出累加器的数据
                    {inc_pc,load_acc,load_pc,rd}<=4'b0000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0010;
                    end
                else
                    begin//否则,就全部为0
                    {inc_pc,load_acc,load_pc,rd}<=4'b0000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                state<=3'b101;
            end
        3'b101:
            begin//第六次:
                if(opcode==ADD || opcode==ANDD ||
                    opcode==XORR || opcode==LDA)
                    begin//如果是上面这些操作,那就要继续进行这些操作,与累加器的输出进行运算
                    {inc_pc,load_acc,load_pc,rd}<=4'b0101;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                else if(opcode==SKZ && zero==1)
                    begin//如果是SKz,先判断是否是零,如果是零就pc+1,否则就全零空操作
                    {inc_pc,load_acc,load_pc,rd}<=4'b1000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                else if(opcode==JMP)
                    begin//如果是JMP,那就pc+1,然后load_pc,锁定目的地址
                    {inc_pc,load_acc,load_pc,rd}<=4'b1010;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                else if(opcode==STO)
                    begin//如果是STO,就将数据写入地址处
                    {inc_pc,load_acc,load_pc,rd}<=4'b0000;
                    {wr,load_ir,datactl_ena,halt}<=4'b1010;
                    end
                else 
                    begin
                    {inc_pc,load_acc,load_pc,rd}<=4'b0000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                state<=3'b110;
            end
        3'b110:
            begin//第七次,空操作
                if(opcode==STO)
                    begin//如果是STO,那么需要使数据控制模块enable
                    {inc_pc,load_acc,load_pc,rd}<=4'b0000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0010;
                    end
                else if(opcode==ADD || opcode==ANDD ||
                    opcode==XORR || opcode==LDA)
                    begin//如果是这些操作码,那就进行read
                    {inc_pc,load_acc,load_pc,rd}<=4'b0001;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                else 
                    begin
                    {inc_pc,load_acc,load_pc,rd}<=4'b0000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                state<=3'b111;
            end
        3'b111:
            begin//第八次
                if(opcode==SKZ && zero==1)
                    begin//如果是SKZ,并且是零,那么就pc+1,否则空操作
                    {inc_pc,load_acc,load_pc,rd}<=4'b1000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                else 
                    begin
                    {inc_pc,load_acc,load_pc,rd}<=4'b0000;
                    {wr,load_ir,datactl_ena,halt}<=4'b0000;
                    end
                state<=3'b000;
            end
        default:
            begin
            {inc_pc,load_acc,load_pc,rd}<=4'b0000;
            {wr,load_ir,datactl_ena,halt}<=4'b0000;
            state<=3'b000;
            end
        endcase
    end
endtask
//————————end of task————————
endmodule
`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 16:22:58
// Design Name: decoder
module decoder(addr,rom_sel,ram_sel);
output rom_sel,ram_sel;
input [12:0] addr;
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
//只有地址码的前两位全1,才选通ram,否则一直是rom
    end
endmodule
`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 16:32:19
// Design Name: ram
module ram(data,addr,read,write,ena);
    output [7:0] data;
    input [9:0] addr;
    input read,write,ena;
    reg [7:0] ram [10'h3ff: 0];
    
    assign data=(read && ena)?ram[addr]:8'hzz;
    
    always @(posedge write)
        begin
        ram[addr]<=data;
        end
        
endmodule
`timescale 1ns / 1ps
//
// Company: 
// Engineer: jeff
// Create Date: 2022/03/24 16:32:19
// Design Name: rom
module rom(data,addr,read,ena);
    output [7:0] data;
    input [12:0] addr;
    input read,ena;
    reg [7:0] memory [13'h1fff : 0];
    wire [7:0] data;
    assign data=(read && ena)?memory[addr]:8'bzzzz_zzzz;
    //定义了一个8位宽,深度为1_1111_1111的存储阵列
    //当read模式、enable时,读addr的存储指令,否则就是高阻态
endmodule
/*******CPU例化***/
`timescale 1ns / 1ps
//
// Company: Jeff
// Engineer: 孟祥健
// 
// Create Date: 2022/03/24 16:46:32
// Design Name: 

module risc_cpu(clk,reset,halt,rd,wr,addr,data); 

 input clk,reset; 
 output rd,wr,addr,halt; 
 inout data; 
 wire clk,reset,halt; 
 wire [7:0] data; 
 wire [12:0] addr; 
 wire rd,wr; 
 wire clk1,fetch,alu_clk; 
 wire [2:0] opcode; 
 wire [12:0] ir_addr,pc_addr; 
 wire [7:0] alu_out,accum; 
 wire zero,inc_pc,load_acc,load_pc,load_ir,data_ena,contr_ena;
 
//1时钟生成器
clkgenerator my_clkgenerator(.clk(clk),.clk1(clk1),.fetch(fetch),
                                 .alu_clk(alu_clk),.rst(reset));
//2指令寄存器
instr_reg my_instr_reg(.opc_iraddr({opcode,ir_addr[12:0]}),.data(data),.ena(load_ir),
                        .clk1(clk1),.rst(reset));
//3累加器
accumulator my_accumulator(.accum(accum),.data(alu_out),.ena(load_ir),
                            .clk1(clk1),.rst(reset));
//4算数逻辑单元:
alu my_alu(.alu_out(alu_out),.zero(zero),.data(data),.accum(accum),
           .alu_clk(alu_clk),.opcode(opcode));
//5状态机控制模块:
state_ctrl my_state_ctrl(.ena(contr_ena),.fetch(fetch),.rst(reset));
//6状态机模块:
state_machine my_machine (.inc_pc(inc_pc),.load_acc(load_acc),.load_pc(load_pc), 
             .rd(rd), .wr(wr), .load_ir(load_ir), .clk1(clk1), 
             .datactl_ena(data_ena), .halt(halt), .zero(zero), 
             .ena(contr_ena),.opcode(opcode));
//7数据控制器(累加器)
data_ctrl my_data_ctrl(.data(data),.in(alu_out),.data_ena(data_ena));
//8地址多路器
addr my_addr(.addr(addr),.fetch(fetch),.ir_addr(ir_addr),.pc_addr(pc_addr));
//9程序计数器
counter my_counter(.pc_addr(pc_addr),.ir_addr(ir_addr),
                    .load(load_pc),.clk(inc_pc),.rst(reset));
endmodule

testbench.v

`timescale 1ns / 100ps 
`define PERIOD 100 // matches clk_gen.v 
//
// Company: 微电子92
// Engineer: 孟祥健
// Create Date: 2022/03/24 17:18:43
// Design Name: tb仿真文件
module tb(); 
 reg reset_req,clock; 
 integer test; 
 reg [(3*8):0] mnemonic; //array that holds 3 8-bit ASCII characters 
 reg [12:0] PC_addr,IR_addr; 
 wire [7:0] data; 
 wire [12:0] addr; 
 wire rd,wr,halt,ram_sel,rom_sel; 
 
//---------------------------------------------------------------------------- 
risc_cpu tb_risc_cpu(.clk(clock),.reset(reset_req),.halt(halt),.rd(rd),
                .wr(wr),.addr(addr),.data(data)); 
ram tb_ram(.addr(addr[9:0]),.read(rd),.write(wr),.ena(ram_sel),.data(data)); 
rom tb_rom(.addr(addr),.read(rd),.ena(rom_sel),.data(data));
decoder tb_decoder(.addr(addr),.ram_sel(ram_sel),.rom_sel(rom_sel)); 
//------------------------------------------------------------------------------

initial 
 begin 
     clock=1; 
     //display time in nanoseconds 
     $timeformat ( -9, 1, " ns", 12); 
     display_debug_message; 
     sys_reset; 
     
     test1; 
     $stop;
     test2; 
     $stop; 
     test3; 
     $stop; 
end

task display_debug_message; 
     begin 
         $display("\n******************************************************"); 
         $display("* THE FOLLOWING DEBUG TASK ARE AVAILABLE: *"); 
         $display("* \"test1; \" to load the 1st diagnostic progran. *"); 
         $display("* \"test2; \" to load the 2nd diagnostic program. *"); 
         $display("* \"test3; \" to load the Fibonacci program. *"); 
         $display("******************************************************\n"); 
     end 
endtask

task test1; 
     begin 
         test = 0; 
         //disable MONITOR; 
         $readmemb ("test1.pro", tb_rom.memory); 
         $display("rom loaded successfully!"); 
         $readmemb("test1.dat",tb_ram.ram); 
         $display("ram loaded successfully!"); 
         #1 test = 1; 
         #14800 ; 
         sys_reset; 
     end 
 endtask
 
task test2; 
     begin 
         test = 0; 
         disable MONITOR; 
         $readmemb("test2.pro",tb_rom.memory); 
         $display("rom loaded successfully!"); 
         $readmemb("test2.dat",tb_ram.ram); 
         $display("ram loaded successfully!"); 
         #1 test = 2; 
         #11600; 
         sys_reset;
     end 
 endtask
 
task test3; 
     begin 
         test = 0; 
         disable MONITOR; 
         $readmemb("test3.pro",tb_rom.memory); 
         $display("rom loaded successfully!"); 
         $readmemb("test3.dat",tb_ram.ram); 
         $display("ram loaded successfully!"); 
         #1 test = 3; 
         #94000; 
         sys_reset; 
     end 
endtask

task sys_reset; 
     begin 
         reset_req = 0; 
         #(`PERIOD*0.7) reset_req = 1; 
         #(1.5*`PERIOD) reset_req = 0; 
     end 
endtask

always @(test) 
 begin: MONITOR 
     case (test) 
         1: begin //display results when running test 1 
             $display("\n*** RUNNING CPUtest1 - The Basic CPU Diagnostic Program ***"); 
             $display("\n TIME PC INSTR ADDR DATA "); 
             $display(" ---------- ---- ----- ----- ----- "); 
             while (test == 1) 
                 @(tb_risc_cpu.my_addr.pc_addr)//fixed 
                 if ((tb_risc_cpu.my_addr.pc_addr%2==1)&& (tb_risc_cpu.my_addr.fetch == 1))
                 //if ((tb_risc_cpu.my_addr.pc_addr%2 == 1)&&(tb_risc_cpu.my_addr.fetch == 1))//fixed 
                    begin 
                         # 60 PC_addr<=tb_risc_cpu.my_addr.pc_addr-1; 
                         IR_addr <=tb_risc_cpu.my_addr.ir_addr; 
                         # 340 $strobe("%t %h %s %h %h",$time,PC_addr,mnemonic,IR_addr,data );
                         //HERE DATA HAS BEEN CHANGED T-CPU-M-REGISTER.DATA 
                     end 
         end
         2: begin 
             $display("\n*** RUNNING CPUtest2 - The Advanced CPU Diagnostic Program ***"); 
             $display("\n TIME PC INSTR ADDR DATA "); 
             $display(" ---------- --- ----- ----- ---- "); 
             while (test == 2) 
                @(tb_risc_cpu.my_addr.pc_addr)
                if ((tb_risc_cpu.my_addr.pc_addr%2 == 1) 
                        && (tb_risc_cpu.my_addr.fetch == 1)) 
                     begin 
                         # 60 PC_addr = tb_risc_cpu.my_addr.pc_addr - 1 ; 
                         IR_addr = tb_risc_cpu.my_addr.ir_addr; 
                         # 340 $strobe("%t %h %s %h %h", $time, PC_addr, 
                         mnemonic, IR_addr, data ); 
                     end 
                     
            end
         3: begin 
                 $display("\n*** RUNNING CPUtest3 - An Executable Program ***"); 
                 $display("*** This program should calculate the fibonacci ***"); 
                 $display("\n TIME FIBONACCI NUMBER"); 
                 $display( " --------- -----------------"); 
                 while (test == 3) 
                     begin 
                         wait ( tb_risc_cpu.my_alu.opcode == 3'h1) // display Fib. No. at end of program loop 
                         $strobe("%t %d", $time,tb_ram.ram[10'h2]); 
                         wait ( tb_risc_cpu.my_alu.opcode != 3'h1); 
                     end 
               end 
     endcase
 end
 
//--------------------------------------------------------------------------------
always @(posedge halt) //STOP when HALT instruction decoded 
     begin 
         #500 
         $display("\n***********************************************************"); 
         $display("* A HALT INSTRUCTION WAS PROCESSED !!! *"); 
         $display("***********************************************************\n"); 
     end 
always #(`PERIOD/2) clock=~clock; 

always @(tb_risc_cpu.my_alu.opcode) 
     //get an ASCII mnemonic for each opcode 
     case(tb_risc_cpu.my_alu.opcode) 
     3'b000 : mnemonic ="HLT"; 
     3'h1 : mnemonic = "SKZ"; 
     3'h2 : mnemonic = "ADD"; 
     3'h3 : mnemonic = "AND"; 
     3'h4 : mnemonic = "XOR"; 
     3'h5 : mnemonic = "LDA"; 
     3'h6 : mnemonic = "STO"; 
     3'h7 : mnemonic = "JMP"; 
     default : mnemonic = "???"; 
     endcase

endmodule


测试

(应该是用ModelSim仿真的,详见原文)
test1.pro

//------------------------------- test1.pro --------------------------------------------------------------------- 
@00 //address statement 
111_00000 
0011_1100 
000_00000
0000_0000 
000_00000
0000_0000 
101_11000
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 value in TEMP 
0000_0010 
101_11000  // 16 LDA DATA_1 
0000_0000
110_11000  // 18 STO TEMP //store zero value 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 if 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 
//-----------------------------test1.pro-------------------------------------------

test1.dat

//-----------------test1.dat开始------------------------------------------------ 
@00 //address statement at RAM 
00000000 // 1800 DATA_1: //constant 00(hex) 
11111111 // 1801 DATA_2: //constant FF(hex) 
10101010 // 1802 TEMP: //variable - starts with AA(hex) 
//------------------------------test1.dat的结束

test2.pro

//------------------test2.pro-----------------------
@00 
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 //FF plus 1 makes -1 
0000_0000 
110_11000 // 16 STO TEMP 
0000_0011 
101_11000 // 18 LDA DATA_1 
0000_0000 
010_11000 // 1a ADD TEMP //-1 plus 1 should make zero 
0000_0011 
001_00000 // 1c SKZ 
0000_0000 
000_00000 // 1e HLT //ADD Doesn't work 
0000_0000 
000_00000 // 20 END: HLT //CONGRATULATIONS - TEST2 PASSED! 
0000_0000 
111_00000 // 22 JMP BEGIN //run test again 
0000_0000 
//-----------------------------test2.pro结束---------------------------------------------

test2.dat

//------------------------------test2.dat开始------------------------------------------------ 
@00 
00000001 // 1800 DATA_1: //constant 1(hex) 
10101010 // 1801 DATA_2: //constant AA(hex) 
11111111 // 1802 DATA_3: //constant FF(hex) 
00000000 // 1803 TEMP: 
//------------------------------test2.dat结束 .-------------------------------------------

test3.pro

//------------------test3.pro-------------------------------------------------------------------- 
@00 
101_11000 // 00 LOOP: LDA FN2 //load value in FN2 into accum 
0000_0001 
110_11000 // 02 STO TEMP //store accumulator in TEMP 
0000_0010 
010_11000 // 04 ADD FN1 //add value in FN1 to accumulator 
0000_0000 
110_11000 // 06 STO FN2 //store result in FN2 
0000_0001 
101_11000 // 08 LDA TEMP //load TEMP into the accumulator 
0000_0010 
110_11000 // 0a STO FN1 //store accumulator in FN1 
0000_0000 
100_11000 // 0c XOR LIMIT //compare accumulator to LIMIT 
0000_0011 
001_00000 // 0e SKZ //if accum = 0, skip to DONE 
0000_0000 
111_00000 // 10 JMP LOOP //jump to address of LOOP 
0000_0000 
000_00000 // 12 DONE: HLT //end of program 
0000_0000 
//-----------------------------test3.pro结束--------------------------------------------

test3.dat

//-----------------test3.dat开始------------------------------------------------ 
@00 
00000001 // 1800 FN1: //data storage for 1st Fib. No. 
00000000 // 1801 FN2: //data storage for 2nd Fib. No. 
00000000 // 1802 TEMP: //temproray data storage 
10010000 // 1803 LIMIT: //max value to calculate 144(dec) 
//-----------------------------test3.pro结束--------------------------------------------

参考资料:

  1. RISC-V官方网站
  2. RISC-V指令集特性手册
posted @ 2022-11-25 17:04  qsBye  阅读(125)  评论(0编辑  收藏  举报