CPU理解笔记

笔者是集成电路专业的本科生,最近不幸对计组和verilog产生兴趣(然后就被现实暴打啦。)

总之被脑力风暴折磨以后,总算写了点笔记(不要靠近IC,会不幸)

(今天要讨论的是RISC架构八位CPU)

图中,包含CPU,存储器,数据和地址总线。CPU主要由算数逻辑单元(ALU, Arithmetic Logic Unit),累加器(accumulator),通用寄存器(registers),程序计数器(PC,Program Counter),指令寄存器(IR,Instruction Register),地址选择器(address multiplexer)组成。存储器这里指主 存储器,分为随机存取存储器RAM(Radom Access Memory)和只读存储器ROM(Read Only Memory)。主存 和CPU之间通过总线访问,总线有地址总线(Address Bus)和数据总线(Data Address)两种。寄存器组也可能被叫做寄存器堆

需要注意的是,我所讨论的是单周期CPU。

至于大家经常听到的“x级流水”,可以参考这个https://blog.csdn.net/weixin_50026222/article/details/125446367

(下面是具体实现)

module PC(
	pc_addr,//取的地址
	clk,//时钟信号
	rst,
	//一边来说,如果想直接指定某个指令
	//可以加上,input jump,jump_ddr,前者判定指定是否有效,jump_addr可以是希望指向的地址啦
	en//高电平信号,监管pc_addr向下移动
);
		input clk, rst, en;
		output reg[7:0] pc_addr;//输出

		always @(posedge clock or negedge rst)
			begin
			  //if(jump)pc_addr<=jump_addr
				if (!rst) pc_addr <= 8'd0;//复位
				else 
					begin
						if (en) pc_addr <= pc_addr + 1;//通过en控制计数器++,比如001+1,变成了010,这就指向了一个新的指令,可以认为类似c语言的指针
						else pc_addr <= pc_addr;//当然了,加几都可以,只要是指向某个指令的地址
					end
			end
/*顺便一说,1)时序电路建模时,用非阻塞赋值。
2)锁存器电路建模时,用非阻塞赋值。
3)用always块建立组合逻辑模型时,用阻塞赋值。
4)在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值。
5)在同一个always块中不要既用非阻塞赋值又用阻塞赋值。
6)不要在一个以上的always块中为同一个变量赋值。
7)用$strobe系统任务来显示用非阻塞赋值的变量值
8)在赋值时不要使用#延迟
-------避免竞争
*/
endmodule


//累加器
module accum(
	in,
	out, 
	ena, 
	clk,
	rst
);//很不幸,你算出来了a+b,但是还不想直接给c,所以把a+b先放在这吧
		input clk, rst, ena;//决定中间值是否更新的信号,可以锁存
		input[7:0] in;
		output reg[7:0] out;
		always @(posedge clk or negedge rst)
		begin
			if (!rst) out <= 8'd0;
			else
				begin
					if (ena) out <= in;
					else out <= out;//如果没有信号激励,结果不会更新,中间值一直保留
				end
		end
endmodule
//地址选择器
module addr_mux(
	addr,
	sel,
	ir_ad,//指令寄存器的输出
	pc_ad);//程序计数器的指令

		input[7:0] ir_ad, pc_ad;
		input sel;//select
		output[7:0] addr;
		assign addr = (sel) ? ir_ad : pc_ad;
endmodule

//算术逻辑单元
module alu(
	alu_out,
	alu_in,
	accum, //这个accum当然是从accum来的,前面我们说到,accum可以保管中间值,也可以根据情况拿出来
	op//此op非彼op,是指令编号啦
);

	input[2:0] op;
	input[7:0] alu_in, accum;
	output reg[7:0] alu_out;
	parameter 
		A = 3'b000,
		B = 3'b001,
		C = 3'b010,
		D = 3'b011,
		E = 3'b100,
		F = 3'b101,
		X = 3'b110,
		Y = 3'b111;
	always @(*)(1444584) 
		begin
			casez(op)//这里自由性很高,完全可以自己定义,
				A: alu_out = accum;
				B: alu_out = ~accum;
				C: alu_out = alu_in^accum;
				D: alu_out = alu_in+accum;
				E: alu_out = accum-alu_in;
				F: alu_out = alu_in&accum;
				X: alu_out = alu_in|accum;
				Y: alu_out = alu_in;
				default: alu_out = 8'bzzzz_zzzz;//避免异常
			endcase
		end
//顺便补充一下casex和casez。假如我们用case找最高位为1的8位二进制数,那得有2^7-1种可能.但是用casez,找1zzz_zzzz就可以了
endmodule
//寄存器们
module reg_32(
	in,
	data,
	write,
	read, 
	addr,
	clk
);
		input write, read, clk;
		input[7:0] in;
		input[7:0] addr;
		output[7:0] data;
		reg[7:0] R[31:0]; //8*32
		wire[4:0] r_addr;
		assign r_addr = addr[4:0];
		assign data = (read) ? R[r_addr] : 8

		always @(posedge clk) 
			begin 
			if (write) R[r_addr]
			end
endmodule


//IR指令寄存器从数据总线上获取数据,根据输入控制信号,根据指令类型将特定指令和地址输出到ALU,通用寄存器 和地址选择器

module ins_reg(
	data, 
	fetch,
	clk, 
	rst, 
	ins, 
	ad1, 
	ad2
);
		input clk, rst;
		input[1:0] fetch;
		input[7:0] data;
		output[2:0] ins;
		output[4:0] ad1;
		output[7:0] ad2;
		reg[7:0] ins_p1, ins_p2;
		reg[2:0] state;
		assign ins = ins_p1[7:5]; //指令标识
		assign ad1 = ins_p1[4:0]; //操作数
		assign ad2 = ins_p2;
		always @(posedge clk or negedge rst) begin
			if (!rst) 
				begin
					ins_p1 <= 8'd0;
					ins_p2 <= 8'd0;
				end
			else 
				begin
					if (fetch == 2'b01)
						begin
							ins_p1 <= data;
							ins_p2 <= ins_p2;
						end
					else if 
						begin
							ins_p1 <= ins_p1;
							ins_p2 <= data;
						end
					else 
						begin
							ins_p1 <= ins_p1;
							ins_p2 <= ins_p2;
						end
				end
				end
					endmodule

、

(控制器等器件和顶部文件什么的日后更新,要考试了orz,寒假一定完整仿真完送上资源)

#IC#
全部评论

相关推荐

2025-12-22 16:31
已编辑
桂林电子科技大学 Python
很奥的前端仔:如果你接了offer 临时又说不去 hr确实要多做一些工作。 当然如果是接offer之前当我没说
点赞 评论 收藏
分享
评论
3
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务