当前位置: 首页 > article >正文

[读书日志]8051软核处理器设计实战(基于FPGA)第七篇:8051软核处理器的测试(verilog+C)

6. 8051软核处理器的验证和使用

为了充分测试8051的性能,我们需要测试每一条指令。在HELLO文件夹中存放了整个测试的C语言工程文件。主函数存放在指令被分为五大类,和上面一样。

在这里插入图片描述

在这里插入图片描述

打开后是这样的文件结构。HELLO.c是主文件,这是里面的代码:

/*------------------------------------------------------------------------------
HELLO.C

Copyright 1995-2005 Keil Software, Inc.
------------------------------------------------------------------------------*/

#include <REG52.H>                /* special function register declarations   */
                                  /* for the intended 8051 derivative         */

#include <stdio.h>                /* prototype declarations for I/O functions */

#include "instruction.h"


/*------------------------------------------------
The main C function.  Program execution starts
here after stack initialization.
------------------------------------------------*/
void main (void) {
  test_status = 1;
	
	instruction_test_all();
	
	
	if (test_status) {
		printf("Test success!\n");
	}else{
		printf("Test failed!\n");
	}
  printf("Test finished!\n");
	kill_self = 1;
  while (1);
}

这里引用了头文件instruction.h,它的实现在Instruction文件夹中,先看一下instruction.c的内容:

#include <REG52.H>
#include <stdio.h> 
#include "instruction.h"

void error(void){
	if (test_status==0) {
    printf("ERROR HERE...\n");
		while(1);
	}
}	

void instruction_test_all(void){
#ifdef ARITHMETIC
  arithmetic();  
#endif
#ifdef LOGICAL
	logical();
#endif
#ifdef TRANSFER
	transfer();
#endif
#ifdef BOOLEAN
	boolean();
#endif
#ifdef PROGRAM
	program();
#endif
}

void arithmetic(void){
#ifdef ADD_A_RN
    add_a_rn();
#endif
#ifdef ADD_A_DI
    add_a_di();
#endif	
#ifdef ADD_A_RI
    add_a_ri();
#endif		
#ifdef ADD_A_DA
    add_a_da();
#endif
#ifdef ADDC_A_RN
    addc_a_rn();
#endif	
#ifdef ADDC_A_DI
    addc_a_di();
#endif		
#ifdef ADDC_A_RI
    addc_a_ri();
#endif
#ifdef ADDC_A_DA
    addc_a_da();
#endif
#ifdef SUBB_A_RN
    subb_a_rn();
#endif
#ifdef SUBB_A_DI
    subb_a_di();
#endif
#ifdef SUBB_A_RI
    subb_a_ri();
#endif
#ifdef SUBB_A_DA
    subb_a_da();
#endif
#ifdef INC_A
    inc_a();
#endif
#ifdef INC_RN
    inc_rn();
#endif
#ifdef INC_DI
    inc_di();
#endif
#ifdef INC_RI
    inc_ri();
#endif
#ifdef INC_DP
    inc_dp();
#endif
#ifdef DEC_A
    dec_a();
#endif
#ifdef DEC_RN
    dec_rn();
#endif
#ifdef DEC_DI
    dec_di();
#endif
#ifdef DEC_RI
    dec_ri();
#endif
#ifdef MULT
    mult();
#endif
#ifdef DIVIDE
    divide();
#endif
#ifdef DA_A
    da_a();
#endif
}

这里只列出了一部分。可以看出通过预编译指令定义了五类指令及每一类的指令。而每一类中具体的指令通过汇编语言使用预编译指令进行编译。它们分布在剩余的几个c文件中。比如算术运算指令如下:

#include <REG52.H>
#include <stdio.h> 
#include "instruction.h"

void add_a_rn(void) {
	printf("ADD_A_RN\n");
	#pragma ASM  
	push psw
	push acc
  mov  psw,#0H	
  setb rs0     
	setb rs1	
  #pragma ENDASM 
	
	#pragma ASM
	mov acc,#01H
	mov R0,#0fH
	add A,R0
  #pragma ENDASM	
	if (ACC!=0x10) test_status = 0;
	if (AC!=1) test_status = 0;
	if (OV!=0) test_status = 0;
	if (CY!=0) test_status = 0;
	AC = 0;
		
	#pragma ASM
	mov acc,#40H
	mov R1,#40H
	add A,R1
	#pragma ENDASM
	if (ACC!=0x80) test_status = 0;
	if (AC!=0) test_status = 0;
	if (OV!=1) test_status = 0;
	if (CY!=0) test_status = 0;
	OV = 0;

	
	#pragma ASM
  mov acc,#80H
	mov R2,#81H
	add A,R2
  #pragma ENDASM
	if (ACC!=0x01) test_status = 0;
	if (AC!=0) test_status = 0;
	if (OV!=1) test_status = 0;
	if (CY!=1) test_status = 0;
	OV = 0;
	CY = 0;
	
	#pragma ASM
  mov acc,#0C0H
	mov R3,#0C2H
	add A,R3
  #pragma ENDASM
	if (ACC!=0x82) test_status = 0;
	if (AC!=0) test_status = 0;
	if (OV!=0) test_status = 0;
	if (CY!=1) test_status = 0;
	CY = 0;	
	
	#pragma ASM 
	pop acc
  pop psw	
  #pragma ENDASM 	
	error();
}

如果每一条指令都通过,最后会打印测试成功字样,如果有任何一条指令执行有误则会导致抛出错误和暂停测试。

将HELLO工程编译后生成了HELLO.bin,这就是我们最后需要使用的文件,将其留存。之后编写一个tb文件,用于对接接口和设置存储空间:

`timescale 1 ns/1 ps
`define PERIOD 10
`define HALF_PERIOD (`PERIOD/2)
//`define TYPE8052
`define CODE_FILE "C:/Users/15661/Desktop/R8051_test/HELLO/HELLO.bin"
module tb;

reg     clk = 1'b0;
always #`HALF_PERIOD clk = ~clk;

reg     rst = 1'b1;
initial #`PERIOD rst = 1'b0;

wire            rom_en;
wire [15:0]     rom_addr;
reg  [7:0]      rom_byte;
reg             rom_vld;

wire            ram_rd_en_data;
wire            ram_rd_en_sfr;
wire            ram_rd_en_xdata;
wire [15:0]     ram_rd_addr;

reg  [7:0]      ram_rd_byte;

wire            ram_wr_en_data;
wire            ram_wr_en_sfr;
wire            ram_wr_en_xdata;
wire [15:0]     ram_wr_addr;
wire [7:0]      ram_wr_byte;


r8051 u_cpu (
    .clk                  (    clk              ),
	.rst                  (    rst              ),
	.cpu_en               (    1'b1             ),
	.cpu_restart          (    1'b0             ),
	
	.rom_en               (    rom_en           ),
	.rom_addr             (    rom_addr         ),
	.rom_byte             (    rom_byte         ),
	.rom_vld              (    rom_vld          ),
	
	.ram_rd_en_data       (    ram_rd_en_data   ),
	.ram_rd_en_sfr        (    ram_rd_en_sfr    ),
	.ram_rd_en_xdata      (    ram_rd_en_xdata  ),
	.ram_rd_addr          (    ram_rd_addr      ),
	.ram_rd_byte          (    ram_rd_byte      ),
	.ram_rd_vld           (    1'b1             ),
	
	.ram_wr_en_data       (    ram_wr_en_data   ),
	.ram_wr_en_sfr        (    ram_wr_en_sfr    ),
	.ram_wr_en_xdata      (    ram_wr_en_xdata  ),
	.ram_wr_addr          (    ram_wr_addr      ),
	.ram_wr_byte          (    ram_wr_byte      )

);

reg [7:0] rom[(1'b1<<16)-1:0];

integer fd,fx;
initial begin
  fd = $fopen(`CODE_FILE,"rb");
  fx = $fread(rom,fd);
  $fclose(fd);
end
	
always @ ( posedge clk )
if ( rom_en )
    rom_byte <=  rom[rom_addr];
else;

always @ ( posedge clk )
rom_vld <=  rom_en;


reg [7:0] data [127:0];
reg [7:0] data_rd_byte;
always @ ( posedge clk )
if ( ram_rd_en_data )
    data_rd_byte <=  data[ram_rd_addr[6:0]];
else;

always @ ( posedge clk )
if ( ram_wr_en_data )
    data[ram_wr_addr[6:0]] <=  ram_wr_byte;
else;

reg [7:0] xdata [127:0];
reg [7:0] xdata_rd_byte;
always @ ( posedge clk )
if ( ram_rd_en_xdata )
    xdata_rd_byte <=  xdata[ram_rd_addr[6:0]];
else;

always @ ( posedge clk )
if ( ram_wr_en_xdata )
    if (( ram_wr_addr[6:0]==8'h7f ) & ram_wr_byte[0] ) begin
	    repeat(1000) @ (posedge clk);
		$display("Test over, simulation is OK!");
		$stop(1);
		end
	else
        xdata[ram_wr_addr[6:0]] <=  ram_wr_byte;
else;

reg [7:0] sfr_rd_byte;

always @ ( posedge clk )
if ( ram_wr_en_sfr & ( ram_wr_addr[7:0]==8'h99 ) )
    $write("%s",ram_wr_byte);
else;


always @ ( posedge clk )
if ( ram_rd_en_sfr ) 
    if ( ram_rd_addr[7:0]==8'h98 )
	    sfr_rd_byte <=  8'h3;
	else if ( ram_rd_addr[7:0]==8'h99 )
	    sfr_rd_byte <=  0;
	else
    begin
        $display($time," ns : --- SFR READ: %2h---",ram_rd_addr[7:0]);
        //$stop;
    end
else;	

always @ ( posedge clk )
if ( ram_wr_en_sfr )
    if(( ram_wr_addr[7:0]==8'h98 )|( ram_wr_addr[7:0]==8'h99 ))
	    #0;
    else	
    begin
    $display($time," ns : --- SFR WRITE: %2h -> %2h---",ram_wr_addr[7:0],ram_wr_byte);
    //$stop;
    end
else;	

reg [1:0] read_flag;
always @ ( posedge clk )
if ( ram_rd_en_sfr )
    read_flag <= 2'b10;
else if ( ram_rd_en_xdata )
    read_flag <= 2'b01;	
else if ( ram_rd_en_data )
    read_flag <= 2'b0;
else;

always @*
if ( read_flag[1] )
    ram_rd_byte = sfr_rd_byte;
else if ( read_flag[0] )
    ram_rd_byte = xdata_rd_byte;
else
    ram_rd_byte = data_rd_byte;

endmodule

将tb.v和R8051.v一起加入Modelsim工程(注意不要导入instruction.v,否则会报错,需要更改的有两处路径,分别是tb中引用HELLO.bin的路径,和R8051.v中对instruction.v引用的路径,更改后即可复现代码。)之后进行仿真,结果正确。

在这里插入图片描述

在这里插入图片描述

至此,我们的8051软核处理器开发基本完毕。之后可以自行修改一些功能,使用这套测试框架进行测试。现在这个软核处理器中没有添加中断,在书中并没有提供添加中断的说明。后续如果有机会将继续更新添加中断的内容。


http://www.kler.cn/a/503982.html

相关文章:

  • Vue2+OpenLayers使用Overlay实现点击获取当前经纬度信息(提供Gitee源码)
  • Redis是单线程还是多线程?
  • 数据结构(Java版)第八期:LinkedList与链表(三)
  • 金融项目实战 04|JMeter实现自动化脚本接口测试及持续集成
  • 设计一篇利用python爬虫获取1688详情API接口的长篇软文
  • Perl语言的网络编程
  • 多商家入驻商城系统架构与功能分析
  • 《鸿蒙Next旅游应用:人工智能赋能个性化与智能导览新体验》
  • workloadSelector 是一种在服务网格(如Istio)中用于选择特定工作负载实例的机制。
  • Kafka权威指南(第2版)读书笔记
  • 【如何从0到1设计测试用例使用Fiddler完成弱网测试】
  • Android 调用系统服务接口获取屏幕投影(需要android.uid.system)
  • 数据结构之顺序结构二叉树(超详解)
  • Codeforces Round 976 (Div. 2) and Divide By Zero 9.0(A-E)
  • 接口测试总结(http与rpc)
  • 【漏洞复现】孚盟云 MailAjax.ashx SQL注入漏洞复现
  • VSCode 搜索 搜不到
  • 【Linux】设备驱动中的ioctl详解
  • 初学stm32 --- II2C_AT24C02,向EEPROM中读写数据
  • 如何通过 Nginx 配置防盗链保护静态资源(详细配置)
  • 抢十八游戏
  • web服务器+selinux实验
  • SQL面试题2:留存率问题
  • 1.14学习
  • 用 Python 从零开始创建神经网络(二十):模型评估
  • 《C++11》nullptr介绍:从NULL说起