单周期CPU实验之代码实现及模拟
2017-04-29 17:22
627 查看
一、单周期CPU代码实现
学习 了这么久,终于可以完整实现了单周期CPU了。以下是相关代码,供大家学习使用。为了方便阅读,先进行一些宏定义。
define.v
`define Add 6'b000000 `define Addi 6'b000001 `define Sub 6'b000010 `define Ori 6'b010000 `define And 6'b010001 `define Or 6'b010010 `define Move 6'b100000 `define Sw 6'b100110 `define Lw 6'b100111 `define Beq 6'b110000 `define Halt 6'b111111 `define AaddB 3'b000 `define AsubB 3'b001 `define BsubA 3'b010 `define AorB 3'b011 `define AandB 3'b100 `define nAandB 3'b101 `define AxorB 3'b110 // 异或 A、B两个值不同则为1,相同则为0 `define AxnorB 3'b111 // 同或 A、B两个值不同则为0,相同则为1
先实现ControlUnit单元。
ControlUnit.v
`include "defines.v" `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Module Name: ControlUnit // Function: give some neccessary signal to other module ////////////////////////////////////////////////////////////////////////////////// module ControlUnit( input wire[5:0] opCode,//6位wire型 input wire zero,//默认为1位 output wire PCWre, output wire ALUSrcB, output wire ALUM2Reg, output wire RegWre, output wire InsMemRW, output wire DataMemRW, output wire ExtSel, output wire PCSrc, output wire RegOut, output wire[2:0] ALUOp //三位操作数 ); //assign连续赋值语句,主要用于组合逻辑电路 //左值数据类型必须为wire //连续赋值语句总是处于激活状态。只要任意一个操作数发生变化,表达式就会被立即重新计算,并且将结果赋给等号左边的线网。 //操作数(即右值)可以是标量或向量的线网或寄存器,也可以是函数的调用。 assign PCWre = (opCode == `Halt)? 0:1;//6`b111111 Verilog HDL语言中常量表示方法,6位2进制数 assign ALUSrcB = (opCode == `Addi || opCode == `Ori || opCode == `Sw || opCode == `Lw)? 1:0; assign ALUM2Reg = (opCode == `Lw)? 1 : 0; assign RegWre = (opCode == `Sw || opCode == `Halt)? 0 : 1; assign InsMemRW = 0; assign DataMemRW = (opCode == `Lw)? 0 : 1; assign ExtSel = (opCode == `Ori)? 0 : 1; assign PCSrc = (opCode == `Beq && zero == 1)? 1 : 0; assign RegOut = (opCode == `Addi || opCode == `Ori || opCode == `Lw)? 0 : 1; assign ALUOp[2] = (opCode == `And)? 1 : 0; assign ALUOp[1] = (opCode == `Or || opCode == `Ori)? 1 : 0; assign ALUOp[0] = (opCode == `Sub || opCode == `Or || opCode == `Ori || opCode == `Beq)? 1 : 0; endmodule
接下来,分别实现各个模块。
PC.v
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Module Name: PC // Function: store the next instruction address. ////////////////////////////////////////////////////////////////////////////////// module PC( input wire clk, //时钟信号 input wire Rst, //重置信号 input wire PCWre, //PC是否更改 input wire PCSrc, //PC指令更新方式 input wire[31:0] immediate, //32位立即数 output reg[31:0] address //下一条指令地址 ); always @(posedge clk or posedge Rst) begin if(Rst == 1) begin address = 0; //初始化 end else if(PCWre == 1) begin if(PCSrc == 1) address = address + 4 + immediate*4; //beq跳转 else address = address + 4; //正常跳转 end else begin address <= address; //Halt指令执行以后,PC不变 end end endmodule
指令存储器比较麻烦,由于不是真正的CPU,需要自己设置指令,以供读取使用。由于32位书写太过麻烦,所以统一转换为16进制,这样只用8位了。
转换表:
instructionMemory.v
`timescale 1ns / 1ps // 时间单位/时间精度(并非指相除的关系) ////////////////////////////////////////////////////////////////////////////////// // Module Name: instructionMemory // Function: store and read the instruction ////////////////////////////////////////////////////////////////////////////////// module instructionMemory( input wire[31:0] pc, input wire InsMemRW, output wire[5:0] op, //操作码 output wire[4:0] rs, //源操作数1寄存器号 output wire[4:0] rt, //源操作数2寄存器号 output wire[4:0] rd, //目的寄存器号 output wire[15:0] immediate //立即数 ); wire[31:0] mem[0:15]; //相当于数组mem[16][32] assign mem[0] = 32'h04010014; assign mem[1] = 32'h40020008; assign mem[2] = 32'h00221800; assign mem[3] = 32'h08412800; assign mem[4] = 32'h44222000; assign mem[5] = 32'h48224000; assign mem[6] = 32'hC0220004; assign mem[7] = 32'h80405800; assign mem[8] = 32'h984B0002; assign mem[9] = 32'h9C410002; assign mem[10] = 32'hC162FFFB; assign mem[11] = 32'hFC000000; assign mem[12] = 32'h00000000; assign mem[13] = 32'h00000000; assign mem[14] = 32'h00000000; assign mem[15] = 32'h00000000; //output assign op = mem[pc[5:2]][31:26]; assign rs = mem[pc[5:2]][25:21]; assign rt = mem[pc[5:2]][20:16]; assign rd = mem[pc[5:2]][15:11]; assign immediate = mem[pc[5:2]][15:0]; endmodule
regFile.v
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Module Name: regFile // Function: create 32 registers and initial them, // then use to store and read some data. ////////////////////////////////////////////////////////////////////////////////// module regFile( input wire clk, input wire RegOut, input wire RegWre, input wire ALUM2Reg, input wire[4:0] rs, input wire[4:0] rt, input wire[4:0] rd, input wire[31:0] dataFromALU, input wire[31:0] dataFromRW, output wire[31:0] Data1, output wire[31:0] Data2 ); wire[4:0] writeReg; wire[31:0] writeData; assign writeReg = RegOut? rd : rt; //写寄存器组寄存器的地址,来自rt字段,相关指令:addi、ori、lw RegOut = 0 //写寄存器组寄存器的地址,来自rd字段,相关指令:add、sub、and、or、move RegOut = 1 assign writeData = ALUM2Reg? dataFromRW : dataFromALU; //只有lw指令情况下,ALUM2Reg = 1 reg[31:0] register[0:31]; integer i; initial begin //寄存器初始化 for(i = 0; i < 32; i = i + 1) register[i] = 0; end //output assign Data1 = register[rs]; //读取寄存器中的值,源操作数1 assign Data2 = register[rt]; //读取寄存器中的值,源操作数2 //write Reg always @(posedge clk or RegOut or RegWre or ALUM2Reg or writeReg or writeData) begin if(RegWre && writeReg) register[writeReg] = writeData; //防止数据写入0寄存器 end endmodule
signZeroExtend.v
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Module Name: signZeroExtend // Function: extend the immediate to 32 bits. ////////////////////////////////////////////////////////////////////////////////// module signZeroExtend( input wire[15:0] immediate, input wire ExtSel, output wire[31:0] out ); assign out[15:0] = immediate; /** *ExtSel = 1 做符号位扩展;如果符号位为1,则扩展位为1,如果符号位为0,则扩展位为0 *ExtSel = 0 做0扩展 **/ assign out[31:16] = ExtSel? (immediate[15]? 16'hffff : 16'h0000) : 16'h0000; endmodule
ALU.v
`include "defines.v" `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Module Name: ALU // Function: implement the logical operation ////////////////////////////////////////////////////////////////////////////////// module ALU( input wire[31:0] ReadData1, //源操作数1 input wire[31:0] ReadData2, //源操作数2 input wire[31:0] ExtData, //扩展立即数 input wire ALUSrcB, //若为1,则为立即数 input wire[2:0] ALUOp, //操作符 output reg zero, //结果标志 output reg[31:0] result //结果 ); wire[31:0] inReadData2; assign inReadData2 = ALUSrcB? ExtData : ReadData2; always@(ReadData1 or ReadData2 or ExtData or ALUSrcB or ALUOp or inReadData2) begin case(ALUOp) `AaddB: begin result = ReadData1 + inReadData2; zero = (result == 0)? 1 : 0; end `AsubB: begin result = ReadData1 - inReadData2; zero = (result == 0)? 1 : 0; end `BsubA: begin result = inReadData2 - ReadData1; zero = (result == 0)? 1 : 0; end `AorB: begin result = ReadData1 | inReadData2; zero = (result == 0)? 1 : 0; end `AandB: begin result = ReadData1 & inReadData2; zero = (result == 0)? 1 : 0; end `nAandB: begin result = (~ReadData1) & inReadData2; zero = (result == 0)? 1 : 0; end `AxorB: begin result = ReadData1 ^ inReadData2; zero = (result == 0)? 1 : 0; end `AxnorB: begin result = ReadData1 ^~ inReadData2; zero = (result == 0)? 1 : 0; end endcase end endmodule
DataMemory.v
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Module Name: DataMemory // Function: store and read the data about result ////////////////////////////////////////////////////////////////////////////////// module DataMemory( input wire[31:0] DAddr, //数据地址 input wire[31:0] DataIn, //输入数据 input wire DataMemRW, //=1,写数据; =0,读数据 output reg[31:0] DataOut //输出数据 ); reg[31:0] memory[0:31]; //初始化 integer i; initial begin for(i = 0; i < 32; i = i + 1) memory[i] = 0; end //读数据 always @(DataMemRW) begin //只要DataMemRW发生变化,则会执行always语句块 if( DataMemRW == 0) //读取数据,直接读取 assign DataOut = memory[DAddr]; end always @(DataMemRW or DAddr or DataIn) begin //除了lw指令外,其他指令下DataMemRW均为1 if(DataMemRW == 1) memory[DAddr] = DataIn; //写入操作数2的值,以Result为地址 end endmodule
各个模块实现了,需要把模块串联起来。所以需要一个顶层模块。
SingalCycleCPU.v
`timescale 1ns / 1ps `include "ALU.v" `include "ControlUnit.v" `include "DataMemory.v" `include "PC.v" `include "instructionMemory.v" `include "regFile.v" `include "signZeroExtend.v" `timescale 1ns / 100ps ////////////////////////////////////////////////////////////////////////////////// // Module Name: SignalCycleCPU // Function: contact all of the module ////////////////////////////////////////////////////////////////////////////////// module SingalCycleCPU( input wire clk, input wire Reset ); wire[5:0] opCode; //操作码 wire[31:0] Data1; //来自寄存器的源操作数1 wire[31:0] Data2; //来自寄存器的源操作数2 wire[31:0] curPC; //目前PC地址 wire[31:0] Result; //ALU运算结果 wire[2:0] ALUOp; //ALU运算操作码 wire[31:0] ExtOut; //扩展后的立即数 wire[31:0] DMOut; //读取的寄存器中的树 lw时使用 wire[15:0] immediate; //立即数,扩展前 wire[4:0] rs, rt, rd; //三个类型的寄存器 wire zero, PCWre, PCSrc, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, RegOut; //controlUnit单元信号 // module PC(clk, Reset, PCWre, PCSrc, immediate, Address); PC pc(clk, Reset, PCWre, PCSrc, ExtOut, curPC); // module ALU(ReadData1, ReadData2, inExt, ALUSrcB, ALUOp, zero, result); ALU alu(Data1, Data2, ExtOut, ALUSrcB, ALUOp, zero, Result); // module controlUnit(opCode, zero, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut, ALUOp); ControlUnit control(opCode, zero, PCWre, ALUSrcB, ALUM2Reg, RegWre, InsMemRW, DataMemRW, ExtSel, PCSrc, RegOut, ALUOp); // module dataMemory(DAddr, DataIn, DataMemRW, DataOut); DataMemory datamemory(Result, Data2, DataMemRW, DMOut); /* module instructionMemory( input [31:0] pc, input InsMemRW, input [5:0] op, input [4:0] rs, rt, rd, output [15:0] immediate);*/ instructionMemory insMem(curPC, InsMemRW, opCode, rs, rt, rd, immediate); // module registerFile(clk, RegOut, RegWre, ALUM2Reg, rs, rt, rd, dataFromALU, dataFromRW, Data1, Data2); regFile registerfile(clk, RegOut,RegWre, ALUM2Reg, rs, rt, rd, Result, DMOut, Data1, Data2); // module signZeroExtend(immediate, ExtSel, out); signZeroExtend ext(immediate, ExtSel, ExtOut); endmodule
接下来,为了测试,还需要一个测试文件。
Test.v
`include "SingalCycleCPU.v" `timescale 1ns / 1ps module Test; reg Reset; //初始化地址 reg clk; /*wire[5:0] opCode; wire[31:0] Out1; wire[31:0] Out2; wire[31:0] curPC; wire[31:0] Result; */ SingalCycleCPU sing( .clk(clk), .Reset(Reset) ); initial begin //Initialize Inputs clk = 1; Reset = 1; #50; clk = !clk; Reset = 0; forever #50 begin // 产生时钟信号 clk = !clk; end end endmodule
都实现了,接下来,就是测试了。
二、测试
测试,需要用到ISE自带的仿真软件ISim。这里,我简单介绍一下它的使用。以下是它的界面:波形图中的数据可能很少,如果想要更多的数据,则可以按一下步骤进行。
选中编写的文件:
该文件只是一个总文件,里面还有很多其他模块文件。展开,可以选择不同模块的数据。接下来,选中需要展示的数据,单击鼠标右键,点击框框的选项,数据即可出现在另一个方形区域。
当然,现在不可见。点击一下带叉叉的放大镜,再点击单条指令运行按钮即可出现图像。
剩下的功能大家自己去尝试吧。
三、其他补充
▶使用ISim调试打开文件。如图,双击框框中的文件即可在右边窗口打开:
设置断点。选中那只手,每句话对应的红色区域单击鼠标,即可设置断点(出现一个小圆点)。点击带叉叉的手,即可取消断点;或者在控制台取消也可以。
调试开始。点击运行(全部运行或者单挑指令运行都可以),接下执行到断点处会自动停止,点击”进入单独调试”按钮即可一步步执行程序,并通过波形图查看变量变化。同C++是一样的。大家自己尝试。
▶小技巧
ISE编译 常常出现不知名的警告;或者你前一次编译时成功的,下面一次却失败了。那么,如果不是代码问题,可以尝试下面的方法解决:
此次试验到此就结束了,希望对大家有帮助。
相关文章推荐
- Control Study -> 键盘模拟鼠标(实现用键盘操作鼠标光标)(示例代码下载)
- PostHttpPage用asp是实现模拟登录效果的代码
- 全代码实现模拟时钟
- 体绘制(Volume Rendering)概述之4:光线投射算法(Ray Casting)实现流程和代码(基于CPU的实现)
- 模拟系统nbtstat 的代码实现
- Control Study -> 键盘模拟鼠标(实现用键盘操作鼠标光标)(示例代码下载)
- 使用C/C++实现Socket聊天程序(代码+实验报告)
- 用多线程实现“生产者-消费者问题”(代码+实验报告)
- 模拟QQ隐藏效果的实现(示例+代码+隐藏类)
- PostHttpPage用asp是实现模拟登录效果的代码
- Control Study -> 键盘模拟鼠标(实现用键盘操作鼠标光标)(示例代码下载)
- 程序员面试题精选(48):两个堆栈模拟队列c++代码实现
- 使用C/C++实现Socket聊天程序(代码+实验报告)
- 异质树的实现——代码+实验报告
- 转载:键盘模拟鼠标(实现用键盘操作鼠标光标)(示例代码下载)
- 键盘模拟鼠标(实现用键盘操作鼠标光标)(示例代码下载)
- 介绍如何通过代码实现模拟按键的函数
- VFP调用API实现代码模拟鼠标.键盘动作