OpenRISC—OR1200最小软核系统仿真
上一篇《OpenRISC—OR1200的编译及ELF文件介绍》主要介绍“上层”的操作,本篇介绍软核OR1200基于Verilog HDL级的仿真。
之前已介绍过,如何使用GNU工具链得到OR1200可执行文件,以及模拟器中运行这个可执行文件,并记录指令执行的信息,通过分析记录信息可以判断程序是否按照预期那样运行。
为了剖析OR1200内部结构,本节设计一个OR1200最小系统,并借助ModelSim仿真,观察OR1200内部执行细节。
使用平台:windows + git + modelsim
1. OR1200 VerilogHDL源码
1.1 最小系统框图如下:
1.2 下载源码
从 github 获取OR1200的verilog HDL源码,如下:
git clone https://github.com/openrisc/or1200
1.3 创建modelsim工程
下载完成后,依次进入 ..\or1200\rtl\verilog 目录,可以看到OR1200相关Verilog HDL的源码实现。
打开modelsim创建工程(名称为:or1200_min_exp ),并添加源码到此工程下。
2. 创建最小系统
因为本节使用的最小系统,某些文件需要新增,某些特性并不需要,因此需要修改对应源码。
2.1 新增 or1200_tb.v
对比书籍中提供的源码和最新github最新,会发现略有区别,如下:
发现原书籍源码,多了 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
(2)格式转换
下载Bin2Mem.c文件Bin2Mem.zip,将生成的mem.bin拷贝到相同目录下,执行如下指令:
./Bin2Mem -f mem.bin -o mem.data
查看mem.data的内容,如下:
4. 查看仿真结果
(1)编译工程文件
(2)启动仿真
(3)添加信号到wave窗口
(4)执行仿真
(5)仿真结果
当指令“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)仿真结果
根据以上仿真结果,可以直观观察到随着时钟的前进,每一条指令都依次经过取指阶段、译码阶段和执行阶段,并且译码的同时,上一条指令正在执行,下一条指令被取到。