赞
踩
大三下期末的计算机组成课程设计要求完成一个简易的CPU设计,为了这个课设,,,不得不自己下了一个Vivado(心疼流量…),自己学了一波Verilog语言,有点东西,发誓再也不碰硬件的我学者语言就像是乡下人进城一样颤抖,,,可惜可惜,这波学过以后估计也是不会用了,记一篇博客起码以后能回忆下
简易CPU设计
报告中对三个硬件结构的要求:
结构 | 详细要求 |
---|---|
运算器 | 算术单元、逻辑单元、运算器综合、溢出判断 |
控制器 | 程序计数器、指令寄存器、数据通路设计、控制字、指令译码器、状态寄存器、控制器综合 |
寄存器组 | 需要即使用 |
这个简易CPU也可以说是一个简易的加减法运算器。由于控制器部分有指令寄存器,那么假设加减两个主要指令都在指令寄存器中,指令寻址的方式可以设定为直接寻址;再考虑控制字等因素,整个CPU的输入和输出可以确定有以下几个部分:
这个课程设计中,运算器部分的要求仅仅是实现加减法运算即可,根据该要求,运算器部分期待有两个输入数据和两个输出数据(运算结果和溢出位判断),本人此次计划完成一个8位的CPU设计,因此每根输入的数据线当为8条,输出结果的数据线为8条,溢出数据线为1条。
此外,由于运算的方式选择可能有两种:加法和减法。因此还需要有1条运算方式选择线。
注意:按照课程设计要求,运算器的加减法都必须使用基本逻辑电路实现,所以有必要以基础逻辑运算描述全加和全减器。详细请见compute.v代码部分的封装。
综合整体考虑的话运算器应该有以下的输入和输出:
控制器部分主控整个CPU的运算方式选择,即输入指令地址,取指令,译码和输出指令的过程,由此,控制器部分原则上应当有一个寄存器组用于存放各种指令,这里出于简单的考虑只做最小化处理。
本程序只设置一个数据寄存器组,事先把所有的数据都写好,仿真时直接取数据核查结果
对于Vivado,查看简易原理图请点这个
cpu_top.v
`timescale 1ns / 1ps module CPU_top( input clk, input rst, input [7:0] CMD, input start, input [7:0]Addr_1, input [7:0]Addr_2, output [7:0] data_out, output out_valid, output overflow ); wire [7:0]Address_1; wire [7:0]Address_2; wire [7:0]compute; wire c_state; wire [7:0]data_1; wire [7:0]data_2; control control_1( .clk(clk), .rst(rst), .start(start), .cmd(CMD), .Addr_1(Addr_1), .Addr_2(Addr_2), .Address_1(Address_1), .Address_2(Address_2), .compute(compute), .c_state(c_state) ); data_ram data_ram_1( .clk(clk), .en(c_state), .rst(rst), .addr_1(Address_1), .addr_2(Address_2), . dout_1(data_1), . dout_2(data_2) ); compute compute_1( .clk(clk ), .rst(rst), .comput(compute), .state(c_state), .data_1(data_1), .data_2(data_2), .out(data_out), .over(overflow), .out_vld(out_valid) ); endmodule
control.v
`timescale 1ns / 1ps module control( input clk, input rst,//复位 input start,//使能 连接en input [7:0] cmd, // 1是加法 2是减法 input [7:0] Addr_1, input [7:0] Addr_2, output [7:0] Address_1, output [7:0] Address_2, output [7:0] compute, // 相当于cmd 1是加法 2是减法 output c_state // 1是 运算有效 0是无效 ); //reg [7:0]ADD ; //reg [7:0]SUB ; parameter ADD =1; parameter SUB =2; reg [7:0] Address_1_t; // 一个数字的地址 reg [7:0] Address_2_t; // 一个数字的地址 reg [7:0] compute_t; // 加减选择 0加 1减 reg c_state_t; // 运算有效位 wire is_add, is_sub; assign is_add = is_same(cmd, ADD); // 加法选择判断 assign is_sub = is_same(cmd, SUB); // 减法选择判断 always@(posedge clk or negedge rst)//时钟上升沿或复位下降沿有效 执行下面的代码 if(~rst ) // 复位低电平有效, begin Address_1_t = 0; // 0赋值给address Address_2_t = 0; compute_t = 0; c_state_t = 0; end else if(start) // enable使能有效 begin Address_1_t = Addr_1; // 输入Addr_1 赋值给输出Address_1_t Address_2_t = Addr_2; if (is_add) // verilog的if判断,条件不允许是表达式,必须是一个变量 begin compute_t = 1; // 向运算器传递加法器选择命令 c_state_t =1; end // compute_t与cmd相同,都是1是加法2是减法 cmd是输入 compute_t是输出 else if (is_sub) begin compute_t = 2; // 向运算器传递减法器选择命令 c_state_t =1; end else c_state_t = 0; //c_state_t 0表示运算无效 1是运算有效 end // 这个模块的最后输出 assign Address_1 = Address_1_t; assign Address_2 = Address_2_t; assign compute = compute_t; assign c_state = c_state_t; // ================================================================================== function is_same; // == 实现 input [7:0] I0,I1; begin is_same = (& (I0 ^~ I1)); end endfunction endmodule
data_ram.v
`timescale 1ns / 1ps module data_ram( input clk, input en, // 使能标志,开始运行后一直置1 input rst, // 复位,仿真时,由rst置1开始运行仿真 input [7:0] addr_1, // 输入地址 input [7:0] addr_2, output [7:0] dout_1, // 输出数据 output [7:0] dout_2 ); reg [7:0] RAM[0:9]; // 10个8bit的存储器 integer cnt; always@(posedge rst) // 复位端上升沿有效执行下面的程序 for(cnt =0; cnt<10; cnt=cnt+1) // 初始化寄存器的数据 begin case(cnt) 0: RAM[0] = 100; 1: RAM[1] = 2; 2: RAM[2] = 3; 3: RAM[3] = 2; 4: RAM[4] = 2; 5: RAM[5] = 50; 6: RAM[6] = 70; 7: RAM[7] = 90; 8: RAM[8] = 10; 9: RAM[9] = 30; endcase end assign dout_1 = en ? RAM[addr_1]:0; assign dout_2 = en ? RAM[addr_2]:0; endmodule
compute.v
`timescale 1ns / 1ps module compute( input clk, input rst, input [7:0] comput, input state, input [7:0] data_1, input [7:0] data_2, output [7:0] out, output over, output reg out_vld ); reg [7:0] out_tmp ,over_tmp;//结果输出 溢出结果 wire add_judge, minus_judge; assign add_judge = is_same(comput, 1, state); // 即(comput==1) && state assign minus_judge = is_same(comput, 2, state); // 即(comput==2) && state always@(posedge clk or negedge rst) if(~rst) begin out_tmp <= 0; out_vld <= 0; end else if(add_judge) // 加法 并且 使能端有效 begin add8(data_1, data_2, out_tmp, over_tmp); out_vld <= 1; // 输出有效置1 end else if(minus_judge) // 减法 并且 使能端有效 begin sub8(data_1, data_2, out_tmp, over_tmp); out_vld <= 1; end else begin out_vld <=0; end // 运算符既不是1也不是2 输出有效置0 // assign over = (out_tmp >255 || out_tmp <0)? 1:0 ; assign over = over_tmp; assign out = out_tmp[7:0]; // ================================================== function is_same; // ==运算符的逻辑门实现 以及 使能状态的判断 input [7:0] I0,I1; input state; begin is_same = (& (I0 ^~ I1)) && state; end endfunction 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
Run Behavioral Simulation
cpu_test.v
`timescale 1ns / 1ps `define clk_period 20 // 时钟周期 module CPU_test( ); reg [7:0]CMD; reg start; reg [7:0]Addr_1; reg [7:0]Addr_2; wire [7:0]data_out; wire out_valid; wire overflow; reg Clk; reg Rst_n; CPU_top CPU_1( .clk(Clk), .rst(Rst_n), .CMD(CMD), .start(start), .Addr_1(Addr_1), .Addr_2(Addr_2), .data_out(data_out), .out_valid(out_valid), .overflow(overflow) ); initial Clk = 1; always#(`clk_period/2)Clk = ~Clk; // 跳变 initial begin Rst_n = 1'b0;//一位二进制数0 #(`clk_period*5); // z在20*5个单位时间后执行下面的 Rst_n = 1'b1; // 一位二进制1 赋值给Rst_n CMD =2; // 减法 start = 1 ; Addr_1 = 1; Addr_2 = 2; #`clk_period; // 编译预处理指令 一个时钟周期后执行下面 #(`clk_period*5); Addr_1 = 3; Addr_2 = 4; #`clk_period; #(`clk_period*5); CMD =1; Addr_1 = 6; Addr_2 = 5; #`clk_period; #(`clk_period*5); Addr_1 = 8; Addr_2 = 0; #`clk_period; #(`clk_period*5); Addr_1 = 9; Addr_2 = 7; #`clk_period; #(`clk_period*5); Addr_1 = 0; Addr_2 = 9; #`clk_period; $stop; end endmodule
大学期间倒数第二个课程设计挂了不少人,这个课设刚拿到的时候显然是蒙蔽的,,,开工的第一步是要试着去构思整个电路,只要每个部分都考虑清楚输入输出和期待的功能,完成它的方式是多种多样的,之前见到班里有很多使用protues和其他的基础电路工具做,本人电脑不知道中了什么邪,protues完全用不了,没有选择对的余地,只能折腾verilog,出了100学费学一波,,,以后”硬件“相逢是路人,溜溜球。
这个CPU其实说不上是简易CPU,缺的东西多了去了,所以记一下这个博客以后某一天有机会了回来填坑
本博客所述工程文件:https://download.csdn.net/download/weixin_42744892/11341020
注意该工程文件的cpu_top.v文件中间有一部分注释掉的变量定义代码,请去掉注释再运行。可以对比上边贴的代码,本博客中的代码是没有问题的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。