赞
踩
本人私人博客:Megalomania,大部分文章会现在博客上传,有不足之处欢迎指正。
这个是我这个学期FPGA的期末大作业,老师说是给两周的时间去写,其实还是在最后一周匆匆赶制出来的
不到DDL不开工。其实吧,要我自己评价是挺不满意的,主要是结构缩水了太多,为了节省代码量,我们甚至连PC指针砍掉,并且将ALU和Control Unit强行拼在了一起,变成了一个单地址指令结构的(严格来说应该也算不上)CPU。可能唯一的优点就是执行快吧…
看的出来其实我们设计的这个原理十分简单,一个只剩下指令寄存器的控制单元,不用计算PC指针,还和运算单元结合在了一起,连在顶层文件中的连接都省了。由于是在家里上课,也没有上板之类的,只要ModuleSim能跑就行,所以把外围内存都删了(其实原理也挺简单的),直接看输入输出波形就完事了。但是内部的存储单元还是得要,不然有许多操作的中间值没地方放,自己写一个就行了,也不要什么IP Core之类的。
输入信号为16位数 别问,问就是强迫症,高8位为指令码,低8位为操作数。
总共就九个指令,全部围绕寄存器A进行:
工程总共有4个文件:输入模块,运算控制模块,RAM模块以及顶层模块
输入模块负责将输入的16位二进制数分割为2个8位二进制数,高8位为指令,低8位为操作数或者RAM地址。
module IDEC( clk, reset, code_in, //指令输入端口 code_addr, //操作码输出端口 data_addr //操作数输出端口 ); input clk,reset; input [15:0] code_in; output [7:0] code_addr; output [7:0] data_addr; reg [7:0] code_addr; reg [7:0] data_addr; always @(posedge clk or negedge reset) begin if(!reset) begin code_addr <= 8'b00000000; data_addr <= 8'b00000000; end else begin code_addr <= code_in [15:8]; data_addr <= code_in [7:0]; end end endmodule
运算控制模块大致代码分为两个部分,前半部分是声明指令是干什么的,后半部分进行相应指令的对应操作,最后的task部分为加减算法。
module IMP( clk, //reset, in_data, //1.操作数输入端口 2.ROM地址输入端口 in_code, //操作码输入端口 dout //操作结果输出端口 ); input clk;//reset; input [7:0] in_data; input [7:0] in_code; output [7:0] dout; reg [7:0] dout; parameter in_ram = 8'b00000001, //A->ROM[in_data] load_num = 8'b00000010, //in_data->A out_ram = 8'b00000011, //ROM[in_data]->A clr = 8'b00000100, //0->A inc = 8'b00000101, //A+1->A dec = 8'b00000110, //A-1->A stay = 8'b00000111, //等待指令 add = 8'b00001000, //A+in_data->A min = 8'b00001001; //A-in_data->A reg [7:0] acc; //A reg wr; //写使能端 reg rd; //读使能端 reg [7:0] addr_RAM; //RAM输入地址 wire [7:0] out_RAM; //RAM输出数据 reg fullout; //溢出位 RAM ram( .addr(addr_RAM), .din(acc), .dout(out_RAM), .wr(wr), .rd(rd) ); //对相应指令进行相应操作 always@(posedge clk ) begin case(in_code) stay:begin acc =acc; end clr:begin acc = 0;dout= acc; end inc:begin acc = acc+1;dout= acc; end dec:begin acc = acc-1;dout= acc; end load_num: begin acc = in_data; dout= acc; end in_ram: begin rd=0; wr=1; addr_RAM=in_data; dout=1; end out_ram: begin rd=1; wr=0; addr_RAM=in_data; acc = out_RAM; dout= acc; end add: begin add8(acc, in_data, acc, fullout); dout=acc; end min: begin sub8(acc, in_data, acc, fullout); dout=acc; end default: ; endcase end task add8; // 8位全加器 input [7:0] a,b; output [7:0] sum; output c_out; integer i; reg c_in; begin c_in = 0; begin for(i=0; i<8; i=i+1) begin add1(a[i], b[i], c_in, sum[i], c_out); c_in = c_out; end end end endtask task add1; // 1位全加器 input a,b,c_in; // 加数、被加数、前一次运算的进位 output sum, c_out; // 本位的和、本位运算后是否有进位 begin sum = a^b^c_in;//异或 c_out = (a & b) | (a & c_in) | (b & c_in); end endtask task sub8; // 8位全减器 input [7:0] a,b; output [7:0] diff; output c_in; // 借位 integer i; reg c_out; begin c_out = 0; for(i=0; i<8; i=i+1) begin sub1(a[i],b[i],c_out,diff[i],c_in); c_out = c_in; end end endtask task sub1; // 1位全减器 input a,b, c_out; // 被减数、 减数、 低位是否向本位借位 output diff, c_in; // 本位减运算结果, 本位是否向高位借位 begin diff = a^b^c_out; c_in = (~a & (b ^ c_out)) | (b & c_out); end endtask endmodule
此模块为我们自己写的RAM存储器,负责存储操作数。
module RAM( addr, din, dout, wr, rd ); input [7:0] addr; //存储器地址 input wr; //写使能端,高电平有效 input rd; //读使能端,高电平有效 input [7:0] din; //数据线输入 output reg [7:0] dout; //数据线输出 reg [7:0] mem [0:255]; //内部的存储空间 always @(wr or rd) begin if ( wr && !rd ) //写操作 begin mem[addr]<= din; end if ( rd && !wr ) //读操作 begin dout <= mem[addr]; end end endmodule
module cpu( clk, n_Rst, data_in, data_out ); input clk; input n_Rst; input [15:0] data_in; //输入指令,低8位操作码地址,高8位操作数地址 output [7:0] data_out; //操作结果输出 wire [7:0] data_out; wire [7:0] cade; wire [7:0] data; //取指操作人为输入 //译码 IDEC idec( .clk(clk), .reset(n_Rst), .code_in(data_in), //指令输入端口 .code_addr(cade), //操作码输出端口 .data_addr(data) //操作数输出端口 ); //执行,数据data 既可以当操作数,也可以当ROM地址 IMP imp( .clk(clk), .in_data(data), //1.操作数输入端口 2.ROM地址输入端口 .in_code(cade), //操作码输入端口 .dout(data_out) //操作结果输出端口 ); endmodule
我们是用ISE额外的ModelSim软件进行波形仿真,使用的testbench文件如下:
module TEXT; // Inputs reg clk; reg n_Rst; reg [15:0] data_in; // Outputs wire [7:0] data_out; // Instantiate the Unit Under Test (UUT) cpu uut ( .clk(clk), .n_Rst(n_Rst), .data_in(data_in), .data_out(data_out) ); initial begin // Initialize Inputs clk = 0; n_Rst = 0; data_in = 0; // Wait 100 ns for global reset to finish #100; #100 n_Rst=1; // Add stimulus here #110 data_in = 16'b0000001011111110; //load_rom 1111 1110//MOV A #0FEH #60 data_in = 16'b0000000100000000; //in_rom 0000 0000 //MOV 00H(ROM0), A #40 data_in = 16'b0000010100000000; //inc A //ADD A #20 data_in = 16'b0000001100000000; //out_rom 0000 0000 //MOV A ,00H(ROM0) #80 data_in = 16'b0000100000000001; //add 0000 0001 //ADD A ,#01H #20 data_in = 16'b0000100100000010; //min 0000 0010 //SUB A ,#02H #20 n_Rst=0; end always #10 clk = ~clk; endmodule
得到的波形图如下所示:
可以看出data_out输出先显示出:
用Synplify Pro以100MHz为仿真输入的结果是:
电路图我就不贴了,就是俩模块。只要输入输出线都连上了基本就没有什么问题。有能力的可以尝试将ALU与Control Unit分开,再加个PC寄存器,就差不多是个常见的CPU结构了。
文章转载请注明本人博客地址:
转载自:Megalomania - 【FPGA】基于FPGA的极简CPU设计
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。