Vivado Verilog语言 简易CPU设计

大三下期末的计算机组成课程设计要求完成一个简易的CPU设计,为了这个课设,,,不得不自己下了一个Vivado(心疼流量…),自己学了一波Verilog语言,有点东西,发誓再也不碰硬件的我学者语言就像是乡下人进城一样颤抖,,,可惜可惜,这波学过以后估计也是不会用了,记一篇博客起码以后能回忆下

文章目录

  • 题目要求
  • 题目分析
    • 总析
    • 运算器部分
    • 控制器
    • 寄存器组
  • 程序与原理图
    • 查看方式
    • 顶层结构
    • 控制器
    • 寄存器组
    • 运算器
  • 仿真与时序
    • 仿真方式
    • 仿真代码
    • 部分仿真结果截图
  • 总结
    • 回顾分析
    • 问题与瑕疵
    • Verilog备注
  • 工程文件地址

题目要求

简易CPU设计

  • 硬性要求
    • 结构:一个运算器、一个控制器、寄存器组
    • 功能:能够从寄存器组中取出两个数到运算器进行加减法运算。
    • 细节:不仅要有总的电路设计原理图,要用基础门电路实现,并形成每一部分的详细原理图
  • 可选要求:
    • 设计微程序用于控制

报告中对三个硬件结构的要求:

结构 详细要求
运算器 算术单元、逻辑单元、运算器综合、溢出判断
控制器 程序计数器、指令寄存器、数据通路设计、控制字、指令译码器、状态寄存器、控制器综合
寄存器组 需要即使用

题目分析

总析

这个简易CPU也可以说是一个简易的加减法运算器。由于控制器部分有指令寄存器,那么假设加减两个主要指令都在指令寄存器中,指令寻址的方式可以设定为直接寻址;再考虑控制字等因素,整个CPU的输入和输出可以确定有以下几个部分:

  • 输入
    • 数据地址1
    • 数据地址2
    • 时钟信号
    • 指令地址线(至少1位)
    • 复位线(1位)
    • 使能状态线(1位)
  • 输出
    • 运算结果线(8位)
    • 溢出判断线(1位)
    • 输出有效线(1位)

运算器部分

这个课程设计中,运算器部分的要求仅仅是实现加减法运算即可,根据该要求,运算器部分期待有两个输入数据两个输出数据(运算结果和溢出位判断),本人此次计划完成一个8位的CPU设计,因此每根输入的数据线当为8条,输出结果的数据线为8条,溢出数据线为1条。
此外,由于运算的方式选择可能有两种:加法和减法。因此还需要有1条运算方式选择线。
注意:按照课程设计要求,运算器的加减法都必须使用基本逻辑电路实现,所以有必要以基础逻辑运算描述全加和全减器。详细请见compute.v代码部分的封装。
综合整体考虑的话运算器应该有以下的输入和输出:

  • 输入
    • 数据线1(8位)
    • 数据线2(8位)
    • 运算命令选择线(8位)
    • 时钟信号线(1位)
    • 复位线(1位)
    • 运算器使能线(1位)
  • 输出
    • 运算结果线(8位)
    • 溢出判断线(1位)
    • 输出有效线(1位)

控制器

控制器部分主控整个CPU的运算方式选择,即输入指令地址,取指令,译码和输出指令的过程,由此,控制器部分原则上应当有一个寄存器组用于存放各种指令,这里出于简单的考虑只做最小化处理。

  • 输入
    • 运算命令选择线(8位)
    • 时钟信号线(1位)
    • 复位线(1位)
    • 运算器使能线(1位)
  • 输出
    • 运算方式线(8位)
    • 运算器使能线(1位)

寄存器组

本程序只设置一个数据寄存器组,事先把所有的数据都写好,仿真时直接取数据核查结果

程序与原理图

查看方式

对于Vivado,查看简易原理图请点这个
Vivado Verilog语言 简易CPU设计_第1张图片

顶层结构

Vivado Verilog语言 简易CPU设计_第2张图片
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

控制器

Vivado Verilog语言 简易CPU设计_第3张图片
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

寄存器组

Vivado Verilog语言 简易CPU设计_第4张图片
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

运算器

Vivado Verilog语言 简易CPU设计_第5张图片
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
Vivado Verilog语言 简易CPU设计_第6张图片

仿真代码

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

部分仿真结果截图

Vivado Verilog语言 简易CPU设计_第7张图片

总结

回顾分析

大学期间倒数第二个课程设计挂了不少人,这个课设刚拿到的时候显然是蒙蔽的,,,开工的第一步是要试着去构思整个电路,只要每个部分都考虑清楚输入输出和期待的功能,完成它的方式是多种多样的,之前见到班里有很多使用protues和其他的基础电路工具做,本人电脑不知道中了什么邪,protues完全用不了,没有选择对的余地,只能折腾verilog,出了100学费学一波,,,以后”硬件“相逢是路人,溜溜球。

问题与瑕疵

这个CPU其实说不上是简易CPU,缺的东西多了去了,所以记一下这个博客以后某一天有机会了回来填坑

Verilog备注

  • 硬件描述性语言
  • 虽然是代码控制,但是电路图还是要关注的,针对代码一行一行的找出对应电路去理解才是真正的会
  • 编程的整体思想是从小到大,一个部件一个module的写,之后调用,每个module应该有自己的电路图

工程文件地址

本博客所述工程文件:https://download.csdn.net/download/weixin_42744892/11341020
注意该工程文件的cpu_top.v文件中间有一部分注释掉的变量定义代码,请去掉注释再运行。可以对比上边贴的代码,本博客中的代码是没有问题的。

你可能感兴趣的:(个人学习)