OpenMPSI--CPU架构

前言

最近研读《自己动手写CPU》,发现很多模块的加入,使得整个工程变得非常奇怪,让人不能理解为什么这样设计,现在我将重新构建整个工程,实现更加快速理解,原书籍使用Verilog代码编写,为了避免抄袭,我使用VHDL重新构建代码。部分模块当前不需要使用,我将会将之去除, 在后续文章中加入


一、OpenMIPS教学版介绍

本书中需要一个或操作,采用五级级流水。
总结说明下

  1. 五级整数流水线,分别是:取指、译码、执行、访存、回写。
  2. 哈佛结构,分开的指令、数据接口。

根据百度百科介绍:中央处理器首先到程序指令存储器中读取程序指令内容,解码后得到数据地址,再到相应的数据存储器中读取数据,并进行下一步的操作(通常是执行)。程序指令存储和数据存储分开,可以使指令和数据有不同的数据宽度。
指令集位于CPU内部,而数据集处于外部为ram接口

  1. 32个32位整数寄存器。
  2. 大端模式。

    大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
    小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。

  3. 大多数指令可以在一个时钟周期内完成。

二、流程介绍

1、取指

CPU工作模式,当开始后会不断从程序存储其中取出指令,其中指令地址由一个加法器进行累加,直到遇到停机指令

程序计数器

程序计数器PC中的数值,用来指示当前指令在主存中的位置。当一条指令被取出后,PC中的数值将根据指令字长度而自动递增。若为单字长指令,则PC+1,若为双字长指令,则PC+2,依此类推。OpenMIPS指令集为4个字节

process(clk,rst)
begin
    if(rst = '1')then   --//cpu全局复位信号
        PC <= (others => '0');
    elsif(clk'event and clk = '1') then
      if(rom_ec_i = '1')then --//停机指令,当遇到停机指令时停止
			PC <= PC + x"4";
      else
         PC <= (others => '0');--//程序开始地址
      end if;
    end if;
end process;
rom_addr_o <= PC;

程序存储器

程序为CPU循环运行程序,固定在rom中不需要进行修改。

subtype word_t is std_logic_vector(7 downto 0);--//8位存储器
type memory_t is array(2**8-1 downto 0) of word_t;
signal rom_data : memory_t := init_rom;--//rom_data中存放的是运行程序的二进制码
rom_data_o <= x"00000000" when (rom_ec_i =0)else (rom_data(to_integer(rom_addr_i)) &
													 rom_data(to_integer(rom_addr_i + x'1')) &
													 rom_data(to_integer(rom_addr_i + x'2')) &
													 rom_data(to_integer(rom_addr_i + x'3')) );

取指仅有地址累加时使用一个周期,而读取指令集时使用组合逻辑不需要时钟

2、译码

指令拆解

op <= rom_data_in(31 downto 26); --//指令码
reg_addr_o <= rom_data_in(25 downto 21); --//读取通用寄存器的地址
ram_wd_o <= rom_data_in(20 downto 16); --//结果保存地址
imm(15 downto 0) <= rom_data_in(15 downto 0); --//立即数参与计算的值
imm(31 downto 16) <= (others => rom_data_in(15));

with op select
	aluop_o <= "00100101" when "001101" ;  --//对运算类型进行赋值
			   "00000000" when "000000"	;  --//默认状态
with op select
	reg1_read_o <= "1" when "001101" ;  --//对寄存器1是否读取有效
			   	   "0" when "000000"	;  --//默认状态
with op select
	reg2_read_o <= "0" when "001101" ;  --//对寄存器2是否读取有效
			   	   "0" when "000000"	;  --//默认状态
with op select
	ram_we_o <= "1" when "001101" ;  --//指令是否有效
			  "0" when "000000"	;  --//默认状态

建立RAM存储器

--创建ram存储列表
subtype word_t is std_logic_vector(31 downto 0);--//8位存储器
type memory_t is array(31 downto 0) of word_t;
signal ram_data : memory_t := (others => (others => '0'));--3232位寄存器

--组合逻辑读取1
process(reg1_read_i & ram_we_i)
	case reg1_read_i & ram_we_i is
		when "00" => reg1_data_o <= x"0000000";
		when "01" => reg1_data_o <= x"0000000";
		when "10" => reg1_data_o <= ram_data(to_intager(reg_addr_i));
		when "11" => reg1_data_o <= ram_wdata_i;
		when others => reg1_data_o <= x"0000000";
	end case
end process
--组合逻辑读取2
process(reg2_read_i & ram_we_i)
	case reg2_read_i & ram_we_i is
		when "00" => reg2_data_o <= x"0000000";
		when "01" => reg2_data_o <= x"0000000";
		when "10" => reg2_data_o <= ram_data(to_intager(reg_addr_i));
		when "11" => reg2_data_o <= ram_wdata_i;
		when others => reg1_data_o <= x"0000000";
	end case
end process

寄存配置参数

process(clk,rst) -- 写入数据
begin
    if(rst = '1')then   --//cpu全局复位信号
        ex_aluop <= "00000000";
        ex_wd_o <= "000000"; --写入地址
        ex_we_o <=1--写入使能
   	elsif(clk'event and clk = '1')then
        ex_aluop <= aluop_i;
        ex_wd_o <= ram_wd_i;
        ex_we_o <= ram_we_i --写入使能
   	end if;
end process;
process(clk,rst) -- 写入数据
begin
    if(rst = '1')then   --//cpu全局复位信号
        ex_reg1 <= x"00000000";
        ex_reg2 <= x"00000000";
   	elsif(clk'event and clk = '1')then
   		case reg1_read_i & reg2_read_i is
   			when "00" => ex_reg1 <= imm;ex_reg2 <= imm;
   			when "01" => ex_reg1 <= imm;ex_reg2 <= reg2_data_i;
   			when "10" => ex_reg1 <= reg1_data_i;ex_reg2 <= imm;
   			when "11" => ex_reg1 <= reg1_data_i;ex_reg2 <= reg1_data_i;
   			when others => NULL;
   		end case;
   	end if;
end process;		

3、执行

执行需要一个周期,直接使用时序逻辑

with ex_aluop select
	result <= ex_reg1 | ex_reg2; when "00100101";
	result <= x'0000000'; when others;
	
process(clk,rst) -- 写入数据
begin
    if(rst = '1')then   --//cpu全局复位信号
    	mem_data <= x"00000000";
    	mem_wd_o <= "000000";
    	mem_we_o <= '0';    	
    elsif(clk'event and clk = '1')then
    	mem_data <= result;
    	mem_wd_o <= ex_wd_i;
    	mem_we_o <= ex_we_i;
    end if
end process

4、访存

根据指令需要,有可能要访问主存,读取操作数,这样就进入了访存取数(Memory,MEM)阶段。
此阶段的任务是:根据指令地址码,得到操作数在主存中的地址,并从主存中读取该操作数用于运算。
程序存放地址
一个或操作不需要访存,但是5级流水会有这个操作,此时打一拍

process(clk,rst) -- 写入数据
begin
    if(rst = '1')then   --//cpu全局复位信号
    	mem_wb_data <= x"00000000";
    	mem_wb_wd_o <= "000000";
    	mem_wb_we_o <= '0';    	
    elsif(clk'event and clk = '1')then
    	mem_wb_data <= mem_data;
    	mem_wb_wd_o <= mem_wd_o;
    	mem_wb_we_o <= mem_we_o;
    end if
end process

5、回写

process(clk,rst) -- 写入数据
begin
    if(rst = '1')then   --//cpu全局复位信号
        ram_data <= (others => (others => '0'));
    elsif(clk'event and clk = '1') then
    	if(ram_we_i = '1')then --//停机指令,当遇到停机指令时停止
			case ram_addr_i is
				when "00000" => ram_data(0) <= x"00000000"; --//默认0寄存器为0
				when others  => ram_data(to_intager(ram_addr_i)) => ram_data <= ram_wdata_i;
			end case;  	 
		else 
			ram_data <= ram_data;
     	end if;
    end if;
end process;

总结

本篇简单的讲解了5级流水,冯诺依曼处理器执行方式,有问题,请在下方评论区指出,本篇代码都在文本上进行编写,可能存在语法错误,可以自行修改,也可以再下方留言

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值