Verilog实现简单的单周期RISC_CPU

碎碎念

        这个的设计我是依照在看的一本课本写的,课本里的编程习惯挺奇怪的,状态机编写思维和我以前的写法不太一样,但是我还是暂且学习他的,然后整体综合后应该有测试和汇编的,我整的比较慢,可能要等一会,在学习一下,而且要快期末了,没这么有空

CPU概述

CPU 即中央处理单元的英文缩写,它是计算机的核心部件。计算机进行信息处理可分为两个步骤:

(1)将数据和程序(即指令序列)输入到计算机的存储器中。

(2)从第一条指令的地址起开始执行该程序,得到所需结果,结束运行。CPU 的作用是协调并控制计算机的各个部件并执行程序的指令序列,使其有条不紊地进行。

因此它必须具有以下基本功能:

①取指令——当程序已在存储器中时,首先根据程序入口地址取出一条程序,为此要发出指令地址及控制信号。

②分析指令——即指令译码,这是对当前取得的指令进行分析,指出它要求什么操作,并产生相应的操作控制命令。

③执行指令——根据分析指令时产生的“操作命令”形成相应的操作控制信号序列,通过运算器、存储器及输入/输出设备的执行,实现每条指令的功能,其中包括对运算结果的处理以及下条指令地址的形成。

将CPU的功能进一步细化,可概括如下:

(1)能对指令进行译码并执行规定的动作;

(2)可以进行算术和逻辑运算;

(3)能与存储器和外设交换数据;

(4)提供整个系统所需要的控制。 尽管各种CPU的性能指标和结构细节各不相同,但它们所能完成的基本功能相同。

由功能分析,可知任何一种CPU 内部结构至少应包含下面这些部件:

(1) 算术逻辑运算部件(ALU);

(2)累加器;

(3)程序计数器;

(4)指令寄存器和译码器;

(5)时序和控制部件。

RISC 即精简指令集计算机(Reduced Instruction Set Computer)的缩写。它是一种 20世纪80年代才出现的CPU,与一般的CPU 相比不仅只是简化了指令系统,而且还通过简化指令系统使计算机的结构更加简单合理,从而提高了运算速度。从实现的途径看,RISC_CPU与一般的CPU 的不同之处在于:它的时序控制信号形成部件是用硬布线逻辑实现的而不是采用微程序控制的方式。所谓硬布线逻辑也就是用触发器和逻辑门直接连线所构成的状态机和组合逻辑,故产生控制序列的速度比用微程序控制方式快得多,因为这样做省去了读取微指令的时间。RISC_CPU也包括上述这些部件。

内部模块

时钟发生器

生成fetch,控制取指令,alu_en控制ALU 

module clkgen(
	input					clk,
	input					rstn,
	output				fetch,							//fetch驱动CPU控制器执行下一个指令
	output				alu_en);							//使能ALU,实现CPU对ALU的稳定控制
	
	parameter				S1 = 8'b0000_0001,
								S2 = 8'b0000_0001,
								S3 = 8'b0000_0001,
								S4 = 8'b0000_0001,
								S5 = 8'b0000_0001,
								S6 = 8'b0000_0001,
								S7 = 8'b0000_0001,
								S8 = 8'b0000_0001,
								IDLE = 8'b0000_0000;
								
	reg [7:0]				state;
	reg						fetch_r;
	reg						alu_en_r;
	
	assign 					fetch = fetch_r;
	assign					alu_en = alu_en_r;
								
	always @(posedge clk or posedge rstn) begin 
		if(rstn) begin
			fetch_r <= 1'b0;
			alu_en_r <= 1'b0;
		end else begin 
			case(state)
				S1 : begin 
					alu_en_r <= 1'b1;
					state <= S2;
				end
				S2 : begin
					alu_en_r <= 1'b0;
					state <= S3;
				end
				S3 : begin
					fetch_r <= 1'b1;
					state <= S4;
				end
				S4 : state <= S5;
				S5 : state <= S6;
				S6 : state <= S7;
				S7 : begin 
					fetch_r <= 1'b0;
					state <= S8;
					end
				S8 : state <= S1;
				IDLE : state <= S1;
			endcase
		end
	end
endmodule

指令寄存器 

在每个时钟上升沿触发,使能时将数据依次放入高/低位 

module ir_register(
	input					clk,
	input					rstn,
	input					en,						//data是否能被接受的使能,保证指令寄存器接受到正确的指令码
	input [7:0]			data,
	output [15:0]		opc_addr);				//将高八位和低八位数据拼接输出,高3位是操作码,低13位是地址
	
	reg [15:0]			opc_addr_r;
	assign opc_addr = opc_addr_r;
	
	reg state;
	
	always @(posedge clk or posedge rstn) begin
		if(rstn) begin
			state <= 1'b0;
			opc_addr_r <= 16'h0000;
		end else begin
			if(en) begin
				case(state) 
					1'b0 : begin
						opc_addr_r [15:8] <= data;
						state <= 1'b1;
					end
					1'b1 : begin
						opc_addr_r [7:0] <= data;
						state <= 1'b0;
					end
					default : begin
						opc_addr_r <= 16'hxxxx;
						state <= 1'bx;
					end
				endcase
			end else state <= 1'b0;
		end
	end
endmodule

累加器 

某些时候暂存某个数据由于运算操作等 

module accum(
	input				clk,
	input				rstn,
	input				en,
	input	[7:0]		data,
	output [7:0]	accum);
	
	reg [7:0]		accum_r;
	
	always @(posedge clk or posedge rstn) begin
		if(rstn) begin
			accum_r <= 8'h00;
		end else if(en) begin
			accum_r <= data;
		end
	end
	
endmodule

算术逻辑运算

ALU,计算机的核心,实现加减算数运算,与异或的逻辑运算和跳转等指令 

module alu(
	input					clk,
	input					en,
	input	[2:0]			opcode,
	input [7:0]			data,accum,
	
	output				alu_out,
	output				zero);
	
	reg [7:0]			alu_out_r;
	reg					zero_r;
	
	parameter			HLT = 3'b000,						//停机指令(Halt),用于停止CPU的执行,暂停所有操作
							SKZ = 3'b001,						//跳过零指令(Skip if Zero),如果ALU的结果为零,则跳转到指定地址执行下一条指令
							ADD = 3'b010,						//加法指令(Add),执行加法运算
							ANDD = 3'b011,						//与操作指令(AND),执行逻辑与运算
							XORR = 3'b100,						//异或操作指令(XOR),执行逻辑异或运算
							LDA = 3'b101,						//装入累加器指令(Load Accumulator),将指定的数据加载到累加器中
							STO = 3'b110,						//存储累加器指令(Store Accumulator),将累加器中的数据存储到指定的位置
							JMP = 3'b111;						//无条件跳转指令(Jump),无条件跳转到指定地址执行下一条指令
							
	assign zero = !accum;
	
	always @(posedge clk) begin
		if(en) begin
			casex(opcode) 
				HLT : alu_out_r <= accum;
				SKZ : alu_out_r <= accum;
				ADD : alu_out_r <= data + accum;
				ANDD : alu_out_r <= data & accum;
				XORR : alu_out_r <= data ^ accum;
				LDA : alu_out_r <= data;
				STO : alu_out_r <= accum;
				JMP : alu_out_r <= accum;
				default : alu_out_r <= 8'hxx;
			endcase
		end
	end
	
endmodule

数据控制器

数据控制器,用于控制累加器和ALU的数据输出,保证不输出时不干扰CPU数据总线  

module datactl(
	input	[7:0]				in,
	input						datactl_en,
	
	output [7:0]			data);
	
	assign data = (datactl_en) ? in : 8'hzz;
endmodule

地址多路器

选择输出是PC(程序计数器)地址或数据/端口地址,由fetch控制,使前四个时钟读取PC,后四个时钟读取IR_ADDR 

module addr(
	input [12:0] 			pc_addr,ir_addr,
	input						fetch,
	output [12:0]			addr);
	
	assign addr = (fetch) ? ir_addr : pc_addr;
	
endmodule

程序计数器

用于提供指令地址,以便读取指令,指令按地址顺序存放在存储器中
有两种途径可形成指令地址:
其一是顺序执行的情况,pc_addr自增
其二是遇到要改变顺序执行程序的情况,例如执行JMP指令后,需要形成新的指令地址,载入ir_addr 

module counter(
	input							clk,						//连接inc_pc
	input							rstn,
	input [12:0]				ir_addr,
	input							load,
	
	output [12:0]				pc_addr);
	
	reg [12:0]					pc_addr_r;
	
	always @(posedge clk or posedge rstn) begin
		if(rstn) begin
			pc_addr_r <= 13'h00000;
		end else begin
			if(load) begin
				pc_addr_r <= ir_addr;
			end else pc_addr_r <= pc_addr_r + 1;
		end
	end
	
endmodule

状态控制器 

连接逻辑

控制器

状态控制器,控制状态机的使能

module machinectl(
	input				clk,rstn,fetch,
	output			en);
	
	reg  				en_r;
	reg 				state;
	
	always @(posedge clk or posedge rstn) begin
		if(rstn) begin
			en_r <= 'b0;
		end else begin
			if(fetch) en_r <= 1'b1;
		end
	end
endmodule

状态机

状态机,CPU的重要控制核心,产生控制信号,在8个时钟周期执行1个指令周期
第0个时钟,CPU状态控制器的输出rd和load _ ir 为高电平,其余均为低电平。指令寄存器寄存由 ROM送来的高8位指令代码
第1个时钟,与上一时钟相比只是inc _ pc从0变为1,故PC增1,ROM送来低8位指令代码,指令寄存器寄存该8位代码
第2个时钟,空操作
第3个时钟,PC增1,指向下一条指令。若操作符为HLT,则输出信号 HLT为高。如果操作符不为HLT,除了PC增一外(指向下一条指令),其他各控制线输出为零
第4个时钟,若操作符为AND,ADD,XOR 或LDA,读相应地址的数据;若为JMP,将目的地址送给程序计数器;若为 STO,输出累加器数据
第5个时钟,若操作符为ANDD,ADD或XORR,算术运算器就进行相应的运算;
若为LDA,就把数据通过算术运算器送给累加器;若为SKZ,先判断累加器的值是否为0,如果为0,PC就增1,否则保持原值;若为JMP,锁存目的地址;若为STO,将数据写入地址处
第6个时钟,空操作
第7个时钟,若操作符为SKZ 且累加器值为0,则PC值再增1,跳过一条指令,否则PC 无变化 

module machine(
	input					clk,en,zero,
	input [2:0]			opcode,
	
	output				inc_pc,								//程序计数器的时钟
	output				load_acc,							//将数据加载到累加器Accum的使能信号
	output				load_pc,								//将一个某个地址加载到程序计数器的使能信号
	output				rd,									//从存储器读取数据
	output				wr,									//向存储器写入数据
	output				load_ir,								//将数据加载到指令寄存器ir_register的使能信号
	output				datactl,								//控制数据路径,可能用于选择数据源或目标,总线控制,使能数据控制器datactl
	output				halt);								//停止CPU的执行,用于响应HLT
	
	reg					inc_pc_r;
	reg               load_acc_r;
	reg               load_pc_r;
	reg					rd_r;
	reg               wr_r;
	reg               load_ir_r;
	reg               datactl_r;
	reg					halt_r;
	reg [2:0] 			state;
	
	parameter			HLT = 3'b000,
							SKZ = 3'b001,
                     ADD = 3'b010,
                     ANDD = 3'b011,
                     XORR = 3'b100,
                     LDA = 3'b101,
                     STO = 3'b110,
                     JMP = 3'b111;
							
							
	always @(negedge clk) begin
		if(!en) begin 
			state <= 3'b000;
			inc_pc_r <= 'b0;
	      load_acc_r <= 'b0;
	      load_pc_r <= 'b0;
			rd_r <= 'b0;
	      wr_r <= 'b0;
			load_ir_r <= 'b0;
	      datactl_r <= 'b0;
			halt_r <= 'b0;
		end else ctl_cycle;
	end
	
	task ctl_cycle;							//状态机任务
		begin 
			case(state) 
				3'b000 : begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0001;
					{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0100;
					state <= 3'b001;
				end
				3'b001 : begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b1001;
					{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0100;
					state <= 3'b010;
				end
				3'b010 : begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
					{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					state <= 3'b011;
				end
				3'b011 : begin
					if(opcode == HLT) begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b1000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0001;
					end else begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b1000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end
					state <= 3'b100;
				end
				3'b100 : begin
					if(opcode == JMP) begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0010;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end else if(opcode == ADD||opcode == ANDD||opcode == XORR||opcode == LDA)begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0001;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end else if(STO) begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0010;
					end else begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end
					state <= 3'b101;
				end
				3'b101 : begin
					if(opcode == ADD||opcode == ANDD||opcode == XORR||opcode == LDA) begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0101;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end else if(opcode == JMP)begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0001;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end else if(opcode == SKZ&&zero == 1) begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b1000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end else if(opcode == STO) begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b1010;
					end else begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end
					state <= 3'b110;
				end
				3'b110 : begin
					if(opcode == STO) begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0010;
					end else if(opcode == ADD||opcode == ANDD||opcode == XORR||opcode == LDA)begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0001;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end else begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end
					state <= 3'b111;
				end
				3'b111 : begin
					if(opcode == SKZ&&zero == 1) begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b1000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end else begin
						{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
						{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					end
					state <= 3'b000;
				end
				default : begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r} <= 4'b0000;
					{wr_r,load_ir_r,datactl_r,halt_r} <= 4'b0000;
					state <= 3'b000;
				end
			endcase
		end
	endtask
	
	
endmodule

外部模块

地址译码器

地址译码器,选择到RAM或ROM的使能 

module addr_decode(
	output			rom_sel,ram_sel,
	input [12:0]	addr);
	
	reg				rom_sel_r,ram_sel_r;
	
	always @(addr) begin
		casex(addr)
			13'b1_1xxx_xxxx_xxxx : {rom_sel_r,ram_sel_r} <= 2'b01;
			13'b0_xxxx_xxxx_xxxx : {rom_sel_r,ram_sel_r} <= 2'b10;
			13'b1_0xxx_xxxx_xxxx : {rom_sel_r,ram_sel_r} <= 2'b10;
			default : {rom_sel_r,ram_sel_r} <= 2'b00;
		endcase
	end
endmodule

 RAM

内存,可读可写,储存数据 

module ram(
    input [9:0] addr,
    inout [7:0] data,
    input en,
    input read,
    input write
);

    reg [7:0] ram [10'h3ff:0];

    // 当使能且读使能为高时,将ram[addr]的数据赋给data;否则data为高阻态
    assign data = (en && read) ? ram[addr] : 8'bz;

    // 写操作
    always @(posedge write) begin
        if (en && write) begin
            ram[addr] <= data;
        end
    end

endmodule

ROM

用于装载测试程序,只读不写 

module rom(
    input en,
    input read,
    input [9:0] addr,
    output [7:0] data
);

    reg [7:0] rom [10'h3ff:0];

    // 当使能且读使能为高时,将rom[addr]的数据赋给data;否则data为高阻态
    assign data = (en && read) ? rom[addr] : 8'bz;

endmodule

 CPU模块的综合

module risc_cpu(
	input					clk,
	input					rstn,				//高复位
	
	output [12:0]		addr,
	output				rd,wr,halt,		
	inout	[7:0]			data,
	
	//下面是调试用
	output [2:0]		opcode_o,
	output [12:0]		ir_addr_o,pc_addr_o,
	output				fetch_o);
	
	wire [12:0]			ir_addr,pc_addr;
	wire [2:0]			opcode;
	wire					alu_en;
	wire					inc_pc;
	wire [7:0]			alu_out,accum;
	wire					fetch;
	wire					load_ir,load_pc,load_acc;
	wire					zero;
	wire					datactl_en,control_en;
	
	assign opcode_o = opcode;
	assign ir_addr_o = ir_addr;
	assign pc_addr_o = pc_addr;
	assign fetch_o = fetch;
	
	clkgen			 			m_clkgen(
		.clk(clk),
		.rstn(rstn),
		.fetch(fetch),
		.alu_en(alu_en));
	
	ir_register  				m_ir_register(
		.clk(clk),
		.rstn(rstn),
		.en(load_ir),
		.opc_addr({opcode,ir_addr}),
		.data(data));
		
	accum							m_accum(
		.clk(clk),
		.rstn(rstn),
		.en(load_acc),
		.data(alu_out),
		.accum(accum));
	
	alu							m_alu(
		.clk(clk),
		.en(alu_en),
		.opcode(opcode),
		.data(data),
		.accum(accum),
		.alu_out(alu_out),
		.zero(zero)
		);
		
	datactl						m_datactl(
		.in(alu_out),
		.datactl_en(datactl_en),
		.data(data));
		
	addr							m_addr(
		.pc_addr(pc_addr),
		.ir_addr(ir_addr),
		.fetch(fetch),
		.addr(addr));
	
	counter						m_counter(
		.clk(inc_pc),
		.rstn(rstn),
		.ir_addr(ir_addr),
		.pc_addr(pc_addr),
		.load(load_pc));
		
	machinectl					m_machinectl(
		.clk(clk),
		.rstn(rstn),
		.fetch(fetch),
		.en(control_en));
		
	machine 						m_machine(
		.clk(clk),
		.en(control_en),
		.zero(zero),
		.opcode(opcode),
		.inc_pc(inc_pc),	
		.load_acc(load_acc),
		.load_pc(load_pc),	
		.rd(rd),		
		.wr(wr),		
		.load_ir(load_ir),	
		.datactl(datactl_en),	
		.halt(halt));
	
endmodule

与外部模块的整体综合 

module cputop;
	reg				rstn,clk;
	integer			test;
	reg [(3*8):0]	mnemonic;
	reg [12:0]		PC_addr,IR_addr;
	wire [12:0]		pc_addr,ir_addr;
	wire [7:0]		data;
	wire [12:0]		addr;
	wire 				rd,wr,halt,ram_sel,rom_sel;
	wire [2:0]		opcode;
	wire				fetch;
	
	cpu				m_cpu(
		.clk(clk),
		.rstm(rstn),
		.halt(halt),
		.rd(rd),
		.wr(wr),
		.addr(addr),
		.data(data),
		.opcode_o(opcode),
      .ir_addr_o(ir_addr),
      .pc_addr_o(pc_addr));
		
	ram				m_ram(
		.addr(addr),
		.en(ram_sel),
		.read(rd),
		.write(wr),
		.data(data));
		
		
	rom 				m_rom(
		.en(rom_sel),
		.read(rd),
		.addr(addr),
		.data(data));
		
		
	addr_decode		m_addr_decode(
		.addr(addr),
		.ram_sel(ram_sel),
		.rom_sel(rom_sel));
		
endmodule

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值