OpenRISC—OR1200最小软核系统仿真
admin 于 2021年01月02日 发表在 软核OR1200

上一篇《OpenRISC—OR1200的编译及ELF文件介绍》主要介绍“上层”的操作,本篇介绍软核OR1200基于Verilog HDL级的仿真。

之前已介绍过,如何使用GNU工具链得到OR1200可执行文件,以及模拟器中运行这个可执行文件,并记录指令执行的信息,通过分析记录信息可以判断程序是否按照预期那样运行。

为了剖析OR1200内部结构,本节设计一个OR1200最小系统,并借助ModelSim仿真,观察OR1200内部执行细节。

使用平台:windows + git + modelsim

1. OR1200 VerilogHDL源码

1.1 最小系统框图如下:

最小框图.png

1.2 下载源码

从 github 获取OR1200的verilog HDL源码,如下:

git clone https://github.com/openrisc/or1200

00.下载verilog源码.png

1.3 创建modelsim工程

下载完成后,依次进入 ..\or1200\rtl\verilog 目录,可以看到OR1200相关Verilog HDL的源码实现。

打开modelsim创建工程(名称为:or1200_min_exp ),并添加源码到此工程下。

02.modelsim_add.png

02_1.modelsim_add.png

2. 创建最小系统 

因为本节使用的最小系统,某些文件需要新增,某些特性并不需要,因此需要修改对应源码。

2.1 新增 or1200_tb.v

对比书籍中提供的源码和最新github最新,会发现略有区别,如下:

03.原书籍源码和当前比较.png

发现原书籍源码,多了 or1200_tb.v 文件。新建 or1200_tb.v  并添加到ModelSim工程。内容如下or1200_tb.zip

`timescale 1ns/100ps

module or1200_tb();

       reg     CLOCK_50;
       reg     rst;
       
       initial begin
               CLOCK_50 = 1'b0;
               forever #10 CLOCK_50 = ~CLOCK_50;
       end
       
       
       
       initial begin
               rst = 1'b0;
               #200 rst= 1'b1;
               #100 rst= 1'b0;
               #1000 $stop;
       end
       
       or1200_top     or1200_top_inst
       (
          .clk_i(CLOCK_50),
          .rst_i(rst),
          .pic_ints_i(20'b0),
          .clmode_i(2'b00),
        
          // Instruction WISHBONE INTERFACE
          .iwb_clk_i(clk_i),    .iwb_rst_i(rst),   .iwb_dat_i(32'b0),       
          .iwb_ack_i(1'b0),     .iwb_err_i(1'b0),  .iwb_rty_i(1'b0),                            
          .iwb_cyc_o(),         .iwb_adr_o(),      .iwb_dat_o(),      
          .iwb_stb_o(),         .iwb_we_o(),       .iwb_sel_o(),                                       
      `ifdef OR1200_WB_CAB
          .iwb_cab_o(),
      `endif

          // Data WISHBONE INTERFACE
          .dwb_clk_i(clk_i),    .dwb_rst_i(rst),   .dwb_dat_i(32'b0),        
          .dwb_ack_i(1'b0),     .dwb_err_i(1'b0),  .dwb_rty_i(1'b0),                                        
          .dwb_cyc_o(),         .dwb_adr_o(),      .dwb_dat_o(),        
          .dwb_stb_o(),         .dwb_we_o(),       .dwb_sel_o(),                                      
       `ifdef OR1200_WB_CAB
          .dwb_cab_o(),
       `endif

          // External Debug Interface
          .dbg_stall_i(1'b0),  .dbg_ewt_i(1'b0),  .dbg_lss_o(),                    
          .dbg_is_o(),         .dbg_wp_o(),       .dbg_bp_o(),                    
          .dbg_stb_i(1'b0),    .dbg_we_i(1'b0),   .dbg_adr_i(0),                    
          .dbg_dat_i(0),       .dbg_dat_o(),      .dbg_ack_o(),

          // Power Management
          .pm_cpustall_i(0),   .pm_clksd_o(),     .pm_dc_gate_o(),
          .pm_ic_gate_o(),     .pm_dmmu_gate_o(), .pm_immu_gate_o(),
          .pm_tt_gate_o(),     .pm_cpu_gate_o(),  .pm_wakeup_o(),
          .pm_lvolt_o()
       );
endmodule

2.2  修改 or_1200_defines.v 

(1)删除以下宏定义前的注释信息:

`define OR1200_NO_DC

//
// Do not implement Insn cache
//
`define OR1200_NO_IC

//
// Do not implement Data MMU
//
`define OR1200_NO_DMMU

//
// Do not implement Insn MMU
//
`define OR1200_NO_IMMU

(2)注释掉以下宏定义:

// Define it if you want DU implemented
//`define OR1200_DU_IMPLEMENTED

// Define it if you want PIC implemented
//`define OR1200_PIC_IMPLEMENTED

// Define it if you want TT implemented
//`define OR1200_TT_IMPLEMENTED

(3)启用QMEM的设置:

`define OR1200_QMEM_IMPLEMENTED

2.3 修改 or1200_qmem_top.v

更改前:

//
// Address comparison whether QMEM was hit
//
`ifdef OR1200_QMEM_IADDR
assign iaddr_qmem_hit = (qmemimmu_adr_i & `OR1200_QMEM_IMASK) == `OR1200_QMEM_IADDR;
`else
assign iaddr_qmem_hit = 1'b0;
`endif

`ifdef OR1200_QMEM_DADDR
assign daddr_qmem_hit = (qmemdmmu_adr_i & `OR1200_QMEM_DMASK) == `OR1200_QMEM_DADDR;
`else
assign daddr_qmem_hit = 1'b0;
`endif

更改后:

//
// Address comparison whether QMEM was hit
//
`ifdef OR1200_QMEM_IADDR
assign iaddr_qmem_hit = 1'b1;
`else
assign iaddr_qmem_hit = 1'b0;
`endif

`ifdef OR1200_QMEM_DADDR
assign daddr_qmem_hit = 1'b1;
`else
assign daddr_qmem_hit = 1'b0;
`endif

3. 运行仿真

3.1 QMEM初始化

尽管步骤1中已创建最小系统,但此时还不可以仿真,因为没有将程序存入QMEM,这个步骤称为QMEM初始化

修改 or1200_spram_2048x32.v ,添加QMEM初始化,如下:

修改前:

//
// Generic RAM's registers and wires
//
reg	[dw-1:0]	mem [(1<<aw)-1:0];	// RAM content
reg	[aw-1:0]	addr_reg;		// RAM address register

修改后:

//
// Generic RAM's registers and wires
//
reg	[dw-1:0]	mem [(1<<aw)-1:0];	// RAM content
reg	[aw-1:0]	addr_reg;		// RAM address register

initial $readmemh ( "mem.data", mem );

$readmemh ( "mem.data", mem ),表示从mem.data中读取数据初始化mem,而这个mem正是QMEM的存储空间。

mem.data是一个文本文件,里面存储的是指令,其每行存储一个32位数据(16进制表示),readmemh函数会将mem.data中的数据依次填写到mem中。

3.1 生成mem.data

(1)生成Binary文件

以上一篇文章中的 example.s 为例,切换到 example.or 的目录下,执行如下指令:

or1k-elf-objcopy -O binary example.or mem.bin

04.创建mem.bin文件.png

(2)格式转换

下载Bin2Mem.c文件Bin2Mem.zip,将生成的mem.bin拷贝到相同目录下,执行如下指令:

./Bin2Mem -f mem.bin -o mem.data

05.生成mem.data文件.png

查看mem.data的内容,如下:

06.mem.data内容查看.png

4. 查看仿真结果

(1)编译工程文件

06_2.compile_all执行.png

(2)启动仿真

07.单击simulate后找到工程.png

(3)添加信号到wave窗口

08.添加需要查看的信号.png

(4)执行仿真

09.run_all仿真.png

(5)仿真结果

10.仿真结果.png

  • 当指令“0x9c21000a”执行完后 r1 变成 0x0a,这条指令正是“l.addi r1,r1,0x0a”

  • 当指令“0xe0420800”执行完后 r2 变成 0x0a,这条指令正是“l.add r2,r2,r1”

  • 其它指令也和仿真结果匹配

6. 观察流水线

(1)添加信号到wave窗口

sim:/or1200_tb/CLOCK_50 sim:/or1200_tb/or1200_top_inst/or1200_cpu/or1200_ctrl/ex_insn 
sim:/or1200_tb/or1200_top_inst/or1200_cpu/or1200_ctrl/id_insn 
sim:/or1200_tb/or1200_top_inst/or1200_cpu/or1200_ctrl/if_insn

(2)or1200_ctrl 模块中三个变量的含义

ex_insn:正在执行的指令;
id_insn:正在译码的指令;
if_insn:当前取到的指令;

(3)仿真结果

11.流水线指令.png

根据以上仿真结果,可以直观观察到随着时钟的前进,每一条指令都依次经过取指阶段、译码阶段和执行阶段,并且译码的同时,上一条指令正在执行,下一条指令被取到。

下一篇:《OpenRISC—OR1200各子模块总览》

注意:本站所有文章除特别说明外,均为原创,转载请务必以超链接方式并注明作者出处。 标签:OpenRISC,modelsim仿真,OR1200最小系统