基于MIPS指令集的单周期处理器设计

基于MIPS指令集的单周期处理器设计

(完整程序获取见文章末尾)

一、项目概述
  1. 设计题目
    设数组存有8个任意字符,将其按顺序拼接得到一个双字(64位),对此双字进行循环左移4位。计算新得到的8个字符中,每个字符中1的个数,并对应存储成新的数组。
  2. 设计内容
    (1)按设计题目采用C语言实现并验证,给出验证结果。
    (2)将验证过的C程序转化为MIPS汇编程序,并转化为机器码。
    (3)面向该汇编程序采用的指令集,采用Verilog分别实现为单周期处理器。
    (4)采用Verilog设计上述处理器的验证环境,并在仿真器上验证,最终给出验证波形图。
    (5)每种处理器的实现要求按照top-down的设计方法,进行模块划分。
    (6)设计结果要求充分考虑成本、性能及存储器存储空间优化。
二、C语言代码及测试结果

根据设计题目的要求设计可C语言代码如下:

//预处理命令
#include <stdio.h>
//主函数
int main(void){ 
  int i,j ;
  char array[8] = "abcdefgh"; 
  char new_array[8];
  char count[8];   //为了编汇编程序时统一使用lb/sb指令,将count也定义位char型

  //进行数组的循环移位操作
  for(i = 0;i<7;i++){   
    new_array[i] = array[i]<<4 | array[i+1]>>4;  
    }//由于字符型是8位的,因此左移四位时,高4位被舍去,低4位补0,右移4位时也类似
  new_array[7] = array[7]<<4 | array[0]>>4;    //对于循环左移,最右侧的字符需要特殊处理
//计算循环移位后的新字符中每个字符中“1”的个数
  for(i = 0;i<8;i++){
    //依次判断每个字符的1的个数
        count[i] = 0;
        for(j = 0;j < 8;j++){
        if((new_array[i] & 1<<j) != 0)  //依次判断字符的每个位是否为1
           count[i]++;
        }  
        printf("array[%d] = %x, new_array[%d] = %x, count[%d] = %d\n",i,
        array[i],i,(unsigned char)new_array[i],i,count[i]);  //不希望编译这条语句时可以使用条件编译指令     
  }
  return 0;
}



程序运行结果如下:
在这里插入图片描述
一个字符为8位,对字符数组进行4位的循环左移,若将字符表示为16进制,则可看为循环左移1位,根据上述运行结果可知,C语言程序实现了题目的要求。

三、汇编代码及机器码

根据C语言编写MIPS汇编程序如下所示:

add $t0,$zero,$zero  #寄存器$t0代表变量i,并初始化为0
add $t1,$zero,$zero   #寄存器$t1代表变量j,并初始化为0
addi $t2,$zero,0#寄存器$t2存储着字符数组array的基地址0
addi $t3,$zero,8  #寄存器$t3存储着字符数组new_array的基地址8
addi $t4,$zero,16 #寄存器$t4存储着int数组count的基地址16
addi $s0, $zero, 1    #$s0存放常数1
addi $s1, $zero, 4    #$s1存放常数4


#对数组array进行循环移位得到新的数组new_array
loop1:	add $t5, $t0, $t2 #将array[i]的地址放入$t5中
	addi $t6, $t5, 1  #将array[i+1]的地址放入$t6中
	add $t7, $t0,$t3 #将new_array[i]的地址放入$t7中
	lb $t8, 0($t5)  #将array[i]存入$t8的低8位
	lb $t5, 0($t6) #将array[i+1]存入$t5的低8位
	sllv $t8, $t8, $s1 #将array[i]逻辑左移4位后放入$t8中
	srlv $t5, $t5, $s1 #将array[i+1]逻辑右移4位后放入$t5中
	or $t5, $t5, $t8 #将移位后的array[i]和array[i+1]按位或后放入$t5中
	sb $t5, 0($t7)  #将$t5的低8位放入内存中new_array[i]位置上
	addi $t0, $t0, 1 # i++
	addi $a3, $zero, 6 
	slt $t5, $a3,$t0 #若6 < i即i > =7,则$t5置1
	beq  $t5, $zero,loop1  #若i <7,则继续循环
lb $t5, 7($t2)  #将array[7]从内存中取出,放入$t5的低8位
lb $t6, 0($t2)  #将array[0]从内存中取出,放入$t6的低8位
sllv $t5, $t5, $s1
srlv $t6, $t6, $s1
or $t5, $t5, $t6
sb $t5, 7($t3) #存入new_array[7]的值

#计算新数组new_array中每个字符的1的个数,并存入数组count
add $t0,$zero,$zero  #前一次循环中i的值被改变,现在要重新置0
loop2:	add $t5, $t0, $t4 #将count[i]的地址放入$t5中
	add $t6, $t0, $t3 #将new_array[i]的地址放入$t6中
	add $s2, $zero, $zero #$s2寄存器暂时存放计数结果
	add $t1, $zero, $zero  #每次进入循环前将j置0
	loop3: sllv $t7, $s0, $t1  #$t7存放1左移j位的结果
		lb $t8, 0($t6) #将new_array[i]存入$t8的低8位
		and $t7, $t7, $t8  
		beq $t7, $zero, else
		addi $s2, $s2, 1   #count++
		else:   sb $s2,0($t5)  #将计数结果存入内存的count[i]对应的位置上
			addi $t1, $t1, 1 #j++
			addi $a3, $zero, 7
			slt $t9, $a3, $t1#若j > 7,则$t9置1
			beq $t9, $zero, loop3 
      addi $t0, $t0, 1  #i++
      addi $a3, $zero, 7
      slt $t5, $a3, $t0 #若i<8,则$t5置1
      beq $t5, $zero, loop2

为了能在MARS仿真器上运行上述MIPS汇编代码,需对汇编代码进行更改:将数组首地址更改为可访问的数据段,并加入将”a,b,c,d,e,f,g,h”存入内存中的汇编语言,具体修改内容如下:

add $t0,$zero,$zero  #寄存器$t0代表变量i,并初始化为0
add $t1,$zero,$zero   #寄存器$t1代表变量j,并初始化为0
addi $t2,$zero,0x10010000  #寄存器$t2存储着字符数组array的基地址0
addi $t3,$zero,0x10010008  #寄存器$t3存储着字符数组new_array的基地址8
addi $t4,$zero,0x10010010   #寄存器$t4存储着int数组count的基地址16
addi $s0, $zero, 1    #$s0存放常数1
addi $s1, $zero, 4    #$s1存放常数4

#依次将"a,b,c,d,e,f,g,h"存入数组array中
addi $a0, $zero, 0x61 #"a"的ASCII码
loop:	add $t5, $t0, $t2 #将array[i]的地址放入$t5中
	add $t6, $a0, $t0 #要存入array[i]的字符
	sb $t6, 0($t5) #依次将"a,b,c,d,e,f,g,h"存入array[i]中
	addi $t0, $t0, 1  #i++
	addi $a3, $zero, 7
       slt $t5, $a3, $t0  #若i<8,则$t5置1
        beq $t5, $zero, loop

#对数组array进行循环移位得到新的数组new_array
add $t0,$zero,$zero   #前一次循环改变了i的值,再次进入循环后重新将i置0	
loop1:	add $t5, $t0, $t2 #将array[i]的地址放入$t5中
	addi $t6, $t5, 1  #将array[i+1]的地址放入$t6中
	add $t7, $t0,$t3 #将new_array[i]的地址放入$t7中
	lb $t8, 0($t5)  #将array[i]存入$t8的低8位
	lb $t5, 0($t6) #将array[i+1]存入$t5的低8位
	sllv $t8, $t8, $s1 #将array[i]逻辑左移4位后放入$t8中
	srlv $t5, $t5, $s1 #将array[i+1]逻辑右移4位后放入$t5中
	or $t5, $t5, $t8 #将移位后的array[i]和array[i+1]按位或后放入$t5中
	sb $t5, 0($t7)  #将$t5的低8位放入内存中new_array[i]位置上
	addi $t0, $t0, 1 # i++
	addi $a3, $zero, 6 
	slt $t5, $a3,$t0 #若6 < i即i > =7,则$t5置1
	beq  $t5, $zero,loop1  #若i <7,则继续循环
lb $t5, 7($t2)  #将array[7]从内存中取出,放入$t5的低8位
lb $t6, 0($t2)  #将array[0]从内存中取出,放入$t6的低8位
sllv $t5, $t5, $s1
srlv $t6, $t6, $s1
or $t5, $t5, $t6
sb $t5, 7($t3) #存入new_array[7]的值

#计算新数组new_array中每个字符的1的个数,并存入数组count
add $t0,$zero,$zero  #前一次循环中i的值被改变,现在要重新置0
loop2:	add $t5, $t0, $t4 #将count[i]的地址放入$t5中
	add $t6, $t0, $t3 #将new_array[i]的地址放入$t6中
	add $s2, $zero, $zero #$s2寄存器暂时存放计数结果
	add $t1, $zero, $zero  #每次进入循环前将j置0
	loop3: sllv $t7, $s0, $t1  #$t7存放1左移j位的结果
		lb $t8, 0($t6) #将new_array[i]存入$t8的低8位
		and $t7, $t7, $t8  
		beq $t7, $zero, else
		addi $s2, $s2, 1   #count++
		else:   sb $s2,0($t5)  #将计数结果存入内存的count[i]对应的位置上
			addi $t1, $t1, 1 #j++
			addi $a3, $zero, 7
			slt $t9, $a3, $t1#若j > 7,则$t9置1
			beq $t9, $zero, loop3 
      addi $t0, $t0, 1  #i++
      addi $a3, $zero, 7
      slt $t5, $a3, $t0 #若i<8,则$t5置1
      beq $t5, $zero, loop2

在Mars中运行汇编代码结果如下:
在这里插入图片描述
根据以上运行结果可知,编写的汇编程序实现了要求的功能。再将汇编语言转化位机器码,如下图所示:

在这里插入图片描述
存入的最后一条指令是ffff_ffff,是自定义的停机指令,当遇到停机指令时pc不再更新,数据存储器和寄存器堆也不能写入数据。

四、指令集描述

根据汇编语言文件可知,包含的指令有:
(1)数据传输指令:
指令 特点
sb I型,opcode:101000(从高位到低位),将寄存器的低八位存入内存
lb I型,opcode:100000,将内存中一个字节放入寄存器低8位
(2)逻辑指令:
指令 含义
sllv R型,funct:000100,sllv $t1, $t2, $t3  $t1 = $t2 << $t3
srlv R型,funct:000110,srlv $t1, $t2, $t3  $t1 = $t2 >> $t3
or R型,funct:100101, 两个源寄存器按位或,结果存入目标
寄存器
and R型,funct:100100,两个源寄存器按位与,结果存入目标寄存器
(3)算术指令:
指令 含义
add R型,funct:100000,两个源寄存器相加,结果放入目标寄存器
addi I型,opcode:001000,源寄存器与立即数相加,结果放入目标寄存器
(4)跳转/转移/比较指令:
指令 含义
beq I型,opcode:000100,比较两个寄存器的值若相等则PC指针跳转到相应位置
slt R型,funct:101010,slt $1, $2, $3,if ($2 < $3),$1 = 1;else $1 = 0;

五、单周期处理器设计

5.1模块示意图与模块代码
5.1.1程序计数器pc
pc中存储着当前指令地址,在本指令集中除了遇到BEQ指令有效的情况是,pc都是每过一个周期加1,模块示意图如下:
在这里插入图片描述
adress_in[31:0]为输入pc的新地址
adress_out[31:0]为当前pc输出
clk为时钟
rst为复位信号
PC模块的Verilog代码如下:

`include "define.v"
//程序计数器模块
module pc(input clk,rst,pc_en,
          input [`BUS_WIDTH-1:0] address_in,
          output reg [`BUS_WIDTH-1:0] address_out); 
always@(posedge clk or negedge rst)  //rst为异步低电平复位信号
 begin
    if(!rst) address_out <= 32'b0;
    else if(!pc_en) ;   //运行到停机指令时,pc_en=0,pc不再更新
    else address_out <= address_in;
 end
endmodule

5.1.2寄存器堆模块
本寄存器组拥有32位寄存器,每一个寄存器为32位,寄存器中存放着数据,通过输入的指令取出指定数据进行相应的操作,模块示意图如下:
在这里插入图片描述
ra[5:0]为第一个读出寄存器的地址
rb[5:0]为第二个读出寄存器的地址
rw[5:0]为写入寄存器的地址
reg_wr为寄存器写使能信号
wr_data[31:0]为写入寄存器的数据
bus_a[31:0]为寄存器的第一个输出数据
bus_b[31:0]为寄存器的第二个输出数据
寄存器堆模块的Verilog代码如下:

`include "define.v"
//寄存器堆模块
module reg_file (input clk,reg_wr,input [`BUS_WIDTH-1:0] wr_data, 
input [`REG_WIDTH-1:0] ra, rb, rw,
output  [`BUS_WIDTH-1:0] bus_a, bus_b);
reg [`BUS_WIDTH-1:0] rom [`BUS_WIDTH-1:0];  //寄存器堆存储结构32x32
assign bus_a = rom[ra];
assign bus_b = rom[rb];
initial
  $readmemh("0.txt",rom,0,31);  //寄存器堆初始化为0,便于仿真,无法综合   
always@(posedge clk)
  if(reg_wr)
  rom[rw] <= wr_data;
  else ; 
endmodule

5.1.3数据存储器模块
数据存储器模块存储数据,以字节为存储单位。其模块示意图如下:
在这里插入图片描述
数据存储器模块存储数据,以字节为存储单位。其模块示意图如下:
data_in[31:0]为输入数据
clk为时钟信号
wr_en为写使能信号
data_out[31:0]为数据输出信号

数据存储器模块的Verilog代码如下:

`include "define.v"
module data_mem (
  input clk, wr_en, 
  input [`BUS_WIDTH-1:0] adr,instr,
  input [`BUS_WIDTH-1:0] data_in,   
  output [`BUS_WIDTH-1:0] data_out  
);
reg [`BYTE-1:0] rom [`DATA_MEM-1:0]; 
/*当停机时在控制台输出数据存储器中的数据
来观察是否实现功能*/
always@(*)
	begin
		if(instr == 32'hffff_ffff)
			begin:for_loop
			integer i ;
			for(i=0;i< 24;i = i+4)
			$display("data_mem[%2d]=%h,data_mem[%2d]=%h,data_mem[%2d]=%h,data_mem[%2d]=%h\n"
			,i,rom[i],i+1,rom[i+1],i+2,rom[i+2],i+3,rom[i+3]);
			end
		else ;
	end 
initial
begin
  $readmemh("ascii.txt",rom,0,7);  
end
assign data_out = {{24{1'b0}},rom[adr]};  
/*由于用到的数据传输指令只有lb和sb
因此在读出数据时输出的高24位置0
写入数据时只取输入数据的低8位*/
always@(posedge clk)  
  begin
    if(wr_en)
    rom[adr] <= data_in[`BYTE-1:0]; 
    else ; 
  end
endmodule

5.1.4指令存储器
指令存储器存放指令,以字为储存单位。其模块示意图如下:
在这里插入图片描述

address_in[31:0]为输入指令11存储器的地址
instr[31:0]输出的指令
指令存储器模块的Verilog代码如下:

`include "define.v"
//指令存储器模块
module instr_mem( input [`BUS_WIDTH-1:0] address_in, 
 output   [`BUS_WIDTH-1:0] instr); 
//指令寄存器按字寻址,最小单元是一个字
 reg [`BUS_WIDTH-1:0] rom [`INSTR-1:0];   
initial
  $readmemh("instru_hex.txt",rom); //将指令存入指令寄存器
assign instr = rom[address_in[`BUS_WIDTH-1:2]];  
//输入的地址是按字节编址时的地址,而指令存储器实质上以字为单位,因此输入地址低两位要舍去
endmodule

5.1.5符号扩展模块
符号扩展模块的功能是将一个16位立即数进行有符号扩展,其模块示意图如下:
在这里插入图片描述

imm16[15:0]为输入的16位立即数
imm32[31:0]为输出的32为立即数
符号扩展模块的Verilog代码如下:

`include "define.v"
//符号扩展模块
module sign_ext(input [15:0] imm16,
                output [`BUS_WIDTH-1:0] imm32);  
assign imm32 = {{16{imm16[15]}},imm16};  //将16位立即数进行有符号扩展到32位
endmodule

5.1.6控制模块
控制模块实现的功能是根据指令中的opcode和funct位得到各个部件的控制信号。其模块示意图如下:
在这里插入图片描述
opcode是MIPS指令的操作码
funct是MIPS的R型指令的功能码
alu_ctr是alu的控制信号
reg_dst是决定写入寄存器是rd还是rt的信号
reg_wr是寄存器写使能信号
alu_src是决定alu第二个操作数是来源于寄存器还是立即数的信号
mem_wr是数据存储器写使能信号
mem_to_reg是决定写入寄存器的数据是来源于alu还是存储器的信号
控制模块的Verilog代码如下:

`include "define.v"
//控制器模块
module control(input [5:0] opcode,funct,output reg [2:0] alu_ctr,
              output  pc_en,reg_dst,reg_wr,alu_src,mem_wr,mem_to_reg);
//当遇到停机指令ffff_ffff时,pc_en = 0
assign pc_en = !((opcode == 6'b111111) && (funct == 6'b111111));
//reg_dst代表的是往寄存器堆中写入数据是写入rd还是rt寄存器的控制信号,对于R型指令写入rd,对于I型指令写入rt 
assign reg_dst = (opcode == 6'b0); 
//reg_wr代表的是寄存器堆的写使能信号,在设计的指令集中所有R型指令均可写入寄存器堆,I型指令中lb和addi可写入
assign reg_wr = (opcode == 6'b0) | (opcode == `LB) | (opcode == `ADDI); 
//alu_src代表控制ALU第二个操作数来源的信号,对于R型指令,BEQ指令,第二个操作数为busb,对于I型指令,第二个操作数为立即数
assign alu_src = (opcode == 6'b0 | opcode == `BEQ);
//mem_wr代表数据存储器写入使能信号,在设计的指令集中只有sb指令可以写入存储器
assign mem_wr = (opcode == `SB);
//mem_to_reg决定写入寄存器的数据是来源与数据存储器还是alu,只有在lb指令时数据来源于存储器
assign mem_to_reg = (opcode != `LB);
//alu_ctr是决定alu做何种运算的控制信号
always@(*)
begin
  if(opcode == 6'b0) //R型指令
  begin
    case(funct)
    `SLLV: alu_ctr = 3'd1;
    `SRLV: alu_ctr = 3'd2;
    `OR:   alu_ctr = 3'd4;
    `AND:  alu_ctr = 3'd3;
    `ADD:  alu_ctr = 3'd0;
    `SLT:  alu_ctr = 3'd6;
    default: alu_ctr = 3'd0;
    endcase 
  end
  else if(opcode == `BEQ) alu_ctr = 3'd5;  //在I型指令中只有BEQ不是作加法运算
  else alu_ctr = 3'd0;
end
endmodule

5.1.7 ALU模块
ALU模块是运算单元,基于本指令集的处理器的ALU至少要实现加法,逻辑左、右移,按位与、或,“是否相等比较(beq)”这6种运算。其模块示意图如下:
在这里插入图片描述
in_a[31:0]为alu的第一个操作数
in_b[31:0]为alu的第二个操作数
alu_ctr[2:0]决定alu做何种运算
branch为分支跳转语句生效的标志信号
result[31:0]是alu的运算结果
ALU模块的Verilog代码如下:

`include "define.v"
//运算器模块
module alu(input [`BUS_WIDTH-1:0] in_a,in_b,
           input [2:0] alu_ctr, //至少要实现6种运算
           output  branch,  //分支跳转标志
           output reg [`BUS_WIDTH-1:0] result);
      parameter ADD = 3'd0, SLL = 3'd1, SRL = 3'd2, AND = 3'd3, OR = 3'd4, EQ = 3'd5, SLT = 3'd6;
      assign branch = (alu_ctr == EQ) & (in_a == in_b); 
      //当alu_ctr=EQ,且两个操作数相等时,说明beq指令生效
           always@(*)
           begin
             case(alu_ctr)
               ADD: result = in_a + in_b;
               SLL: result = in_b << in_a; //在仿真时发现汇编指令sllv $1,$2,$3分别对应于rd,rt,rs 
               SRL: result = in_b >> in_a; //而一般的R型指令的对应关系为rd,rs,rt
               AND: result = in_a & in_b;
               OR:  result = in_a | in_b;
               SLT: result = (in_a < in_b) ? 32'b1 : 32'b0;
               default: result = 32'b0;
             endcase              
           end
endmodule

5.1.8 其他模块
除了上述主要模块以外,还有一些简单的小模块,其Verilog代码如下:

//对一些常用的常量进行宏定义
`define BUS_WIDTH 32  //定义总线位宽位32
`define INSTR 50  //定义指令存储器大小为50,可存储50条32位指令
`define DATA_MEM 24 //定义数据存储期大小为24,可存储24个字节
`define REG_WIDTH 5  //寄存器堆共有32个,需要的地址线为5位
`define BYTE 8 
`define SB 6'b101000
`define LB 6'b100000
`define SLLV 6'b000100
`define SRLV 6'b000110
`define OR 6'b100101
`define AND 6'b101000
`define ADD 6'b100000
`define ADDI 6'b001000
`define BEQ 6'b000100
`define SLT 6'b101010
//输入数据位宽为5的2选1选择器
module mux_5(input [4:0] rd, rt,
            input sel,output [4:0] rw);
      assign rw = sel ? rd : rt;
endmodule
//位宽为32的2选1选择器
module mux_32(input [31:0] rd, rt,
            input sel,output [31:0] rw);
      assign rw = sel ? rd : rt;
endmodule

各个模块的连接示意图如下所示:
在这里插入图片描述
根据模块连接图,可写出顶层模块如下所示:

`include "define.v"
`include "mux_32.v"
`include "pc.v"
`include "instr_mem.v"
`include "mux_5.v"
`include "alu.v"
`include "sign_ext.v"
`include "reg_file.v"
`include "data_mem.v"
`include "control.v"
module top(input clk,rst);
wire branch,reg_dst,alu_src,reg_wr,mem_to_reg,mem_wr,pc_en;
wire [`BUS_WIDTH-1:0] new_pc,pc,instr,operand2,imm32,result,bus_a,bus_b,reg_data,mem_out;
wire [2:0] alu_ctr;
wire [4:0] rw;
pc pc_i(.clk(clk),.rst(rst),.address_in(new_pc),.address_out(pc),.pc_en(pc_en)); //输出pc
instr_mem inst_mem_i(.address_in(pc),.instr(instr));  //取值
sign_ext sign_ext_i(.imm16(instr[15:0]),.imm32(imm32)); //立即数符号扩展
mux_32 mux_1(.rd(pc+32'd4+imm32*4),.rt(pc+32'd4),.sel(branch),.rw(new_pc)); //得到下一pc
mux_5 mux_2(.rd(instr[15:11]),.rt(instr[20:16]),.rw(rw),.sel(reg_dst)); //确定写寄存器
mux_32 mux_4(.rd(result),.rt(mem_out),.rw(reg_data),.sel(mem_to_reg));  //确定寄存器堆写入数据的来源
reg_file reg_file_i(.bus_a(bus_a),.bus_b(bus_b),.clk(clk),.reg_wr(reg_wr),
.wr_data(reg_data),.rw(rw),.ra(instr[25:21]),.rb(instr[20:16])); //寄存器堆写入与读出 
mux_32 mux_3(.rd(bus_b),.rt(imm32),.rw(operand2),.sel(alu_src));  //确定ALU第二个操作数
alu alu_i(.in_a(bus_a),.in_b(operand2),.alu_ctr(alu_ctr),.branch(branch),.result(result)); //alu计算
data_mem  data_mem_i(.data_in(bus_b),.clk(clk),.wr_en(mem_wr),.adr(result),.data_out(mem_out),.instr(instr)); 
//数据寄存器的写入与读出
control control_i(.opcode(instr[31:26]),.funct(instr[5:0]),.alu_ctr(alu_ctr),.pc_en(pc_en), //控制信号产生
.reg_dst(reg_dst),.reg_wr(reg_wr),.alu_src(alu_src),.mem_wr(mem_wr),.mem_to_reg(mem_to_reg));  
endmodule

5.2 仿真验证
由于在编写数据存储器和指令存储器模块时,已经利用$readmemh系统函数将其初始化,所以testbench中只需要加入时钟信号即可编写testbench如下:

`timescale 100ns/1ns
`include "top.v"
module test;
reg clk = 1'b0, rst = 1'b1; //信号定义
top top_i(.clk(clk),.rst(rst));//待测模块实例化
initial
begin
 #1 rst = 1'b0;
 #1 rst = 1'b1;
 #10000 $finish;
end
initial
  forever #5 clk = !clk;
initial
begin
  $dumpfile("test.vcd");
  $dumpvars(0,test);
end
endmodule

在ISE中进行仿真,得到的结果如下图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
根据仿真结果可知,数据存储的的[7:0]存储着原字符数组,而[15:8]被写入了经过循环移位后的新字符数组,[23:16]被写入了新字符数组中每个字符中的1的个数,且数据与C语言程序运行结果一致,因此可认为设计的单周期处理器实现了要求的功能。

六、易出错的地方
  1. 为了便于仿真时观察实验结果,在许多模块中加入了不可综合的语句,若要进行综合,并将程序烧录进FPGA中则需要删除这些不可综合的语句。
  2. 一般的设计处理器的流程是先确定需要哪些指令,在根据分析每条指令的数据通路、控制通路,如下图所示
    在这里插入图片描述
    而在本实验中是根据具体的问题要求来设计指令集,汇编程序要用到哪些指令,在微处理器中就考虑哪些指令。
  3. 使用对内存初始化函数readmemh(16进制数据)时,要注意的是数据文件(txt)需要在项目文件夹中,否则就要写出文件的全路径,还需注意的是全路径要用"/“符号,而windows下用的是”"。
  4. 可能要用到的MIPS指令集知识
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一般来说,R型指令的汇编语言和机器码的对应关系为:
add $1, $2, $3 其中$1,$2,$3分别对应机器码的rd,rs,rt位置,
而sllv与srlv指令有些特殊,其对应关系为:
sllv $1, $2, $2 其中$1,$2,$3分别对应机器码的rd,rt,rs位置。

七、完整程序下载

https://download.csdn.net/download/qq_45439159/13119543

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值