写本篇文章的初心:自己从设计到实现简单cpu的过程走了很多弯路,希望我的设计能帮助到一些人,我会给出本实验的完整代码文件。
1.设计目的
利用quartus||工具设计实现一个简易的CPU,首先设计好相应的部件,通过连线,实现求累加和的功能,本实验求取的累加和为1+2+3+...+10 = 55。
2.开发工具选择
Quartus II 13.0 (64-bit)
3.指令系统设计
3.1指令格式
3.2指令类型
指令 | 作用 | 数据流动 |
取数指令(Load0) | 在主存中取操作数存到AC中 | (M)→ AC |
取数指令(Load1) | 将要累加的数据个数送入寄存器CX | (M)→ CX |
取数指令(Load2) | 将要累加的数据的内存地址单元号送入BX | (M)→ BX |
加法指令(Add) | 将AC与BX中的数据相加,结果送入AC | (AC)+ (BX)→ AC |
BX自加一(INC) | BX自加1 | (BX)+ 1 → BX |
CX自减一(Dec) | CX自减1 | (CX)- 1 → CX |
转移指令(JNZ) | 判断Z_flag是否为0,为0就跳转到Add,否则执行Store | / |
存储指令(Store) | 将结果存到主存中 | (AC)→ M |
停机指令 | 停机 | / |
3.3寻址方式
Load0和Load1采用了直接寻址:在指令格式的地址的字段中直接给出操作数在内存中的地址。
Load2采用了立即数寻址:把一个数直接在指令里面给出来,然后把这个数赋值给目标操作数。
3.4指令流程
最一开始,先让一小段reset=“1”,为了让PC从0开始,PC指向0,开始执行第一条指令,PC存的是指令在主存RAM中的地址。
3.4.1 指令1(Load0)
- 目的:将数字1先放到AC寄存器中。
-
实现:PC中存着指令1在内存中的地址(16个0),把此地址给MAR,MAR → RAM,RAM → MDR,MDR → IR,IR中现在就是指令1,IR根据指令1的4位操作码去执行Load0的内容,根据本指令的后12位地址去找到要取的数字1在主存中的位置,取出来后给AC寄存器,IR → MAR → RAM → MDR,MDR将数据送到主线 → AC
3.4.2 指令2(Load1)
- 目的:取数字9送入到CX中。
-
实现:因为指令1已经将1放入了AC中,所以要是想实现1+2+3+...+10,就需要相加9次而不是10次,具体数据流动如指令1内容。
3.4.3 指令3(Load2)
- 目的:取数字2放入到BX中。
-
实现:因为指令1已经将1送入到了AC寄存器中,所以将2从主存中取出放入到BX中,为了之后AC中的1和BX中的2相加。
3.4.4 指令4(Add)
- 实现:将AC中的数与BX中的数相加,结果再送入到AC中,AC再与BX中的数字相加,这样就实现了累加的效果。
3.4.5 指令5(INC)
-
实现:让BX中的一开始的数字2,相加完后,让2自加1,变成3,实现累加1的效果。
3.4.6 指令6(Dec)
-
实现:一共累加9次,每加一次,CX中的数字就自减1,将CX中的当前数字做判断,如果这个数为0,说明程序该结束了,若不是,则程序的PC应质量Add指令,进入循环,知道加完,CX将判断的结果(Z_flag)送入到CU,控制器控制转移还是程序结束。
3.4.7 指令7(JNZ)
-
实现:控制器判断Z_flag是否为0,若为0,则说明程序该结束了;若为1,则将PC修改为Add接着累加。
3.4.8 指令8(Store)
- 实现:若Z_flag为0,累加完毕,将结果保存到主存中。
3.4.9 指令9(Stop)
- 实现:停机,不用写任何操作。
4.数据通路的设计
5.主存储器的设计
代码:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY Ram_16 IS
PORT (
reset : IN STD_LOGIC;
cp : IN STD_LOGIC;--时钟
MemR : IN STD_LOGIC;
MemW : IN STD_LOGIC;
MAR_IN: IN STD_LOGIC_VECTOR(15 DOWNTO 0);
MDR_IN: IN STD_LOGIC_VECTOR(15 DOWNTO 0);
MDR_OUT:OUT STD_LOGIC_VECTOR(15 DOWNTO 0) --buffer
);
END Ram_16;
ARCHITECTURE ONE OF Ram_16 IS
SUBTYPE ram_word IS STD_LOGIC_VECTOR(0 TO 15 );
TYPE ram_type IS ARRAY (0 TO (31)) OF ram_word;
SIGNAL ram : ram_type;
CONSTANT ram_start: ram_type :=( --ram_start初始化整个数组
0=> "0000"&"000000001000",--load0
1=> "0001"&"000000001001",--load1
2=> "0010"&"000000000010",--load2
3=> "0011"&"000000000000",--add
4=> "0100"&"000000000000",--INC
5=> "0101"&"000000000000",--Dec
6=> "0110"&"000000000011",--Jnz
7=> "0111"&"000000010011",--store
8=> "0000000000000001", --取第一个数“1”
9=> "0000000000001001", --取累计加多少个数“9”
OTHERS => (OTHERS =>'0'));
begin
PROCESS(memR,memW,reset) --去掉时钟
begin
if reset = '1' then
ram <= ram_start;
MDR_out <= "0000000000000000";
--elsif rising_edge(cp) then --时钟上升沿有效
end if;
if memR='1' THEN
MDR_OUT <= ram(conv_integer(MAR_IN));
end if;
if memW = '1' THEN
ram(conv_integer(MAR_IN)) <= MDR_IN;
MDR_OUT <= MDR_IN;
end if;
end process;
end;
6.控制器的设计
代码:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY CU IS
PORT(CLK :IN STD_LOGIC;
RESET:IN STD_LOGIC;
IR:IN STD_LOGIC_VECTOR(15 downto 0);--4位操作码,12位地址码
z_flag :in std_LOGIC; --循环剩余次数,0就停机
PC_bus:OUT STD_LOGIC;
LoadMAR:OUT STD_LOGIC;
IncPC:OUT STD_LOGIC;
MemR:OUT STD_LOGIC;
LoadMDR:OUT STD_LOGIC;
selM:OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
MDR_bus:OUT STD_LOGIC;
LoadIR:OUT STD_LOGIC;
Addr_bus:OUT STD_LOGIC;
AC_bus:OUT STD_LOGIC;
Memw:OUT STD_LOGIC;
LoadAC:OUT STD_LOGIC;
LoadCX:OUT STD_LOGIC;--
LoadBX:OUT STD_LOGIC;--
INCBX:OUT STD_LOGIC;--
BX_BUS:OUT STD_LOGIC;
INCCX:OUT STD_LOGIC;--
selA:OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
EN_ADD:OUT STD_LOGIC;
loadpc:OUT STD_LOGIC;
STATE_OUT:OUT STD_LOGIC_VECTOR(3 DOWNTO 0)
);
END CU;
ARCHITECTURE A OF CU IS
TYPE STATE IS (S0,S1,S2,S3,S4,S5,S6,S7,S8,S9,S10,S11,S12,S13,S14,S15);
SIGNAL PRESENTSTATE :STATE;
SIGNAL NEXTSTATE :STATE;
Signal OP : std_logic_vector(3 downto 0);
--Signal Z_flag : std_logic;
BEGIN
OP <= IR(15 downto 12);
SWITCHTONEXTSTATE:PROCESS(CLK,resET)
BEGIN
IF resET = '1' THEN PRESENTSTATE <= S0;
ELSIF CLK'EVENT AND CLK='1' THEN
PRESENTSTATE<=NEXTSTATE;
END IF;
END PROCESS SWITCHTONEXTSTATE;
CHANGESTATEMODE:PROCESS(PRESENTSTATE,z_flag,IR)
BEGIN
loadpc <= '0';
PC_bus <= '0';
LoadMAR <= '0';
IncPC <= '0';
MemR <= '0';
LoadMDR <= '0';
selM <= "00";
MDR_bus <= '0';
LoadIR <= '0';
Addr_bus <= '0';
AC_bus <= '0';
Memw <= '0';
LoadAC <= '0';
selA <= "00";
LoadCX <= '0';--
LoadBX <= '0';--
INCBX <= '0';--
INCCX <= '0';--
bx_BUS <= '0';
en_ADD <= '0';
CASE PRESENTSTATE IS
WHEN S0=>
PC_bus <= '1';
LoadMAR <= '1';
IncPC <= '1';
NEXTSTATE <= S1;
STATE_OUT <= "0000";
WHEN S1=>
MemR <= '1';
LoadMDR <= '1';
selM <= "10";
NEXTSTATE <= S2;
STATE_OUT <= "0001";
WHEN S2=>
MDR_bus <= '1';
LoadIR <= '1';
NEXTSTATE <= S3;
STATE_OUT <= "0010";
WHEN S3=>
Addr_bus <= '1';
LoadMAR <= '1';
STATE_OUT <= "0011";
IF OP = ("0010") THEN
NEXTSTATE <= S8;--Load2
ELSIF OP = ("0011") THEN
NEXTSTATE <= S7;--add
ELSIF OP = ("0100") THEN
NEXTSTATE <= S9;--Inc
ELSIF OP = ("0101") THEN
NEXTSTATE <= S10;--Dec
ELSIF OP = ("0111") THEN
NEXTSTATE <= S11;--Store
ELSIF OP = ("0110") THEN
IF Z_flag = '1'
THEN NEXTSTATE <= S14;
ELSE NEXTSTATE <= S11;
End if;
ELSE NEXTSTATE <= S4;
End IF;
WHEN S4=>
MEMR <= '1';
LoadMDR <= '1';
SelM <= "10";
STATE_OUT <= "0100";
IF OP = "0000" THEN
NEXTSTATE <= S5;--Load0
ELSIF OP = "0001" THEN
NEXTSTATE <= S6;--Load1
End IF;
WHEN S5 =>--Load0
MDR_bus <= '1';
LoadAC <= '1';
SelA <= "10";
NEXTSTATE <= S0;
STATE_OUT <= "0101";
WHEN S6 =>--Load1
mdr_bus <= '1';
loadCX <= '1';
NEXTSTATE <= S0;
STATE_OUT <= "0110";
WHEN S7 =>--add
bx_BUS <= '1';
EN_ADD <= '1';
NEXTSTATE <= S15;
STATE_OUT <= "0111";
WHEN S8 =>--Load2
LoadBX <= '1';
NEXTSTATE <= S0;
STATE_OUT <= "1000";
WHEN S9 =>--INC
INCBX <= '1';
NEXTSTATE <= S0;
STATE_OUT <= "1001";
WHEN S10 =>--Dec
INCCX <= '1';
NEXTSTATE <= S0;
STATE_OUT <= "1010";
WHEN S11 =>--Store
AC_bus <= '1';
SelM <= "01";
LoadMDR <= '1';
NEXTSTATE <= S12;
STATE_OUT <= "1011";
WHEN S12 =>--Store
Memw <= '1';
STATE_OUT <= "1100";
IF OP <= "0110" THEN
NEXTSTATE <= S13;--Stop
ELSIF OP = "0111" THEN
NEXTSTATE <= S0;
End IF;
WHEN S13 =>--Stop
STATE_OUT <= "1101";
WHEN S14 =>--Z_flag != 0
loadpc <= '1';
STATE_OUT <= "1110";
NEXTSTATE <= S0;
WHEN S15 =>--add_2
loadAC <= '1';
selA <= "01";
STATE_OUT <= "1111";
NEXTSTATE <= S0;
END CASE;
END PROCESS CHANGESTATEMODE;
END A;
7.IR的设计
代码:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity IR is
PORT(
reset : IN STD_LOGIC;
cp : IN STD_LOGIC;
LoadIR : IN STD_LOGIC; --使能端
InData : IN STD_LOGIC_VECTOR(15 DOWNTO 0); --输入
addr_bus : in std_LOGIC;
OutData1 : BUFFER STD_LOGIC_VECTOR(15 DOWNTO 0); --输出
OutData2 : BUFFER STD_LOGIC_VECTOR(3 DOWNTO 0) --输出OP
);
END IR;
ARCHITECTURE BE_IR OF IR IS
BEGIN
PROCESS (reset,cp,LoadIR)
BEGIN
IF reset='1' THEN --复位
OutData1 <= "0000000000000000";
OutData2 <= "0000";--相当于OP了
ELSE
IF cp'event AND cp='1' AND LoadIR = '1' then
OutData1 <= INData;
OutData2 <= InData(15 downto 12);--四位的OP
-- OutData3 <= InData(11);--addr_bus输入
END IF;
END IF;
END PROCESS;
END BE_IR;
8.MAR的设计
代码:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity IR is
PORT(
reset : IN STD_LOGIC;
cp : IN STD_LOGIC;
LoadIR : IN STD_LOGIC; --使能端
InData : IN STD_LOGIC_VECTOR(15 DOWNTO 0); --输入
addr_bus : in std_LOGIC;
OutData1 : BUFFER STD_LOGIC_VECTOR(15 DOWNTO 0); --输出
OutData2 : BUFFER STD_LOGIC_VECTOR(3 DOWNTO 0) --输出OP
);
END IR;
ARCHITECTURE BE_IR OF IR IS
BEGIN
PROCESS (reset,cp,LoadIR)
BEGIN
IF reset='1' THEN --复位
OutData1 <= "0000000000000000";
OutData2 <= "0000";--相当于OP了
ELSE
IF cp'event AND cp='1' AND LoadIR = '1' then
OutData1 <= INData;
OutData2 <= InData(15 downto 12);--四位的OP
-- OutData3 <= InData(11);--addr_bus输入
END IF;
END IF;
END PROCESS;
END BE_IR;
9.MDR的设计
代码:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
ENTITY MDR IS
PORT
(
CP:IN STD_LOGIC;
reset : IN STD_LOGIC;
load_MDR : IN STD_LOGIC;
MDR_BUS:IN STD_LOGIC;
MDR_IN: IN STD_LOGIC_VECTOR(15 DOWNTO 0);
MDR_OUT: BUFFER STD_LOGIC_VECTOR(15 DOWNTO 0)
);
END MDR;
ARCHITECTURE one OF MDR IS
BEGIN
PROCESS (CP)
BEGIN
IF reset = '1' THEN
MDR_OUT <= "0000000000000000";
ELSIF (cp'event AND cp='1') and load_MDR = '1' THEN ---falling_edge
MDR_OUT<=MDR_IN;
END IF;
END PROCESS;
END;
10.寄存器BX的设计
代码:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
-- 寄存器B实体,负责存储计算结果并将结果送入ALU的另一个输入端
entity BX is
Port ( clk : in STD_LOGIC;
reset : in STD_LOGIC;
register_b_in : in STD_LOGIC_VECTOR (15 downto 0);
load_b : in STD_LOGIC;
Inc_BX:in STD_LOGIC;
BX_BUS:IN STD_LOGIC;
register_b_out : out STD_LOGIC_VECTOR (15 downto 0));
end BX;
-- 寄存器B行为
architecture behavioral of BX is
begin
process (clk, reset)
variable tmp_b : STD_LOGIC_VECTOR (15 downto 0):= (others => '0');
begin
if reset = '1' then
tmp_b := (others => '0');
elsif rising_edge(clk) then
if load_b = '1' then
tmp_b := "0000" & register_b_in(11 downto 0);
elsif Inc_BX = '1' then
tmp_b := tmp_b + 1;
end if;
end if;
register_b_out <= tmp_b;
end process;
end behavioral;
11.寄存器CX的设计
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
entity CX is
port (
input_data : in std_logic_vector(15 downto 0); -- 输入信号,二进制正整数
loadCX:in std_logic;
INC_CX:in std_logic;
clk : in std_logic; -- 时钟信号
reset : in std_logic; -- 复位信号
output_signal : out std_logic; -- 输出信号,给控制器,相当于z-flag
z_flag:out std_logic_vector(15 downto 0)
);
end entity CX;
architecture Behavioral of CX is
signal temp : std_logic_vector(15 downto 0);-- 临时变量,用于存储输入信号
begin
process(clk, reset)
begin
if rising_edge(clk) then
if loadCX = '1' then
temp <= INPut_data;
end if;
if INC_CX = '1' then
temp <= temp - 1;
end if;
if temp > 0 then
z_flag <= temp;
output_signal <= '1'; --说明还没有累加结束,应该PC应该转移到ADD指令,进入循环,接着加
elsif temp = 0 then
z_flag <= temp;
output_signal <= '0'; --说明累加9次结束了,应该结束程序了
end if;
end if;
end process;
-- 初始将输出信号设置为低电平
end architecture Behavioral;
12.寄存器AC的设计
代码:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
-- 寄存器A实体,用于向ALU提供输入
entity AC is
Port ( clk : in STD_LOGIC;
reset : in STD_LOGIC;
register_a_in : in STD_LOGIC_VECTOR (15 downto 0);
load_ac : in STD_LOGIC;
AC_BUS:IN STD_LOGIC;
register_a_out : out STD_LOGIC_VECTOR (15 downto 0));
end AC;
-- 寄存器A行为
architecture behavioral of AC is
begin
process (clk, reset)
variable tmp_a : STD_LOGIC_VECTOR (15 downto 0):= (others => '0');
begin
if reset = '1' then
tmp_a := (others => '0');
elsif rising_edge(clk) then
if load_ac = '1' then
tmp_a := register_a_in;
end if;
end if;
register_a_out <= tmp_a;
end process;
end behavioral;
13.ALU的设计
代码:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;
use IEEE.STD_LOGIC_ARITH.ALL;
entity ALU is
Port ( clk : in STD_LOGIC;--时钟
rst : in STD_LOGIC;--复位
R_R_W_ADD : IN STD_LOGIC;--使能端
In_A : in STD_LOGIC_VECTOR (15 downto 0);--操作数A
In_B : in STD_LOGIC_VECTOR (15 downto 0);--操作数B
Out_Y : OUT STD_LOGIC_VECTOR (15 downto 0);--输出
OP : in STD_LOGIC_VECTOR(3 downto 0);--操作码
Flags :out STD_LOGIC_VECTOR(3 DOWNTO 0));--标志位为OF/CF/ZF/SF
end ALU;
architecture Behavioral of ALU is
signal jieguo:std_LOGIC_VECTOR(15 downto 0);
begin
process (IN_A,IN_B,R_R_W_ADD,jieguo)
begin
if R_R_W_ADD = '1' then
jieguo <= in_A + in_B;
out_Y <= JIEguo;
end if;
end process;
end Behavioral;
14.PC的设计
代码:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY PC IS
PORT (
reset : IN STD_LOGIC;
cp : IN STD_LOGIC;--时钟
load_PC : IN STD_LOGIC;
PC_BUS:IN STD_LOGIC;
Inc_PC : IN STD_LOGIC;
--PC_IN: IN STD_LOGIC_VECTOR(15 DOWNTO 0);
PC_OUT:BUFfer STD_LOGIC_VECTOR(15 DOWNTO 0) --buffer
);
END PC;
architecture PC_ar of PC is
begin
process (cp,reset)
variable data : STD_LOGIC_VECTOR (15 downto 0):= (others => '0');
begin
if (reset = '1') then
data := "0000000000000000";
elsif rising_edge(cp) then
if load_PC = '1' then --转移
data := "0000000000000011";
elsif inc_PC = '1' then
data := data + 1; --PC自加一
end if;
end if;
PC_OUT <= data;
end process;
end PC_ar;
15.选择器MUX_1的设计
代码:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY MUX_1 IS
PORT(
sel1 : STD_LOGIC_VECTOR(1 DOWNTO 0);
P1,Q1 : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
Read_data : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) --输出数据
);
END MUX_1;
architecture BEMUX_1 of MUX_1 is
begin
process(P1,Q1)
begin
if sel1 = "10" then
Read_data <= Q1; -- 当选择信号为 '0' 时,输出为 input_1
else
Read_data <= P1; -- 当选择信号为 '1' 时,输出为 input_2
end if;
end process;
end BEMUX_1;
16.选择器MUX_2的设计
代码:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity MUX_2 is
port(
lod:in std_logic_vector(1 downto 0);
FROM_RAM,FROM_AC:in std_logic_vector(15 downto 0);
data_out:out std_logic_vector(15 downto 0));
end MUX_2;
architecture xuanzeqi_ar of MUX_2 is
begin
process(FROM_AC,FROM_RAM)--选择器是组合逻辑器件,不需要时钟
begin
if lod="10" then
data_out<=FROM_RAM;
elsif lod="01" then
data_out<=FROM_AC;
end if;
end process;
end xuanzeqi_ar;
17.三态门部件的设计
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY santaimen IS
PORT
(
CP:IN STD_LOGIC;
en : IN STD_LOGIC;
STM_IN: IN STD_LOGIC_VECTOR(15 DOWNTO 0);
STM_OUT: BUFFER STD_LOGIC_VECTOR(15 DOWNTO 0)
);
END santaimen;
ARCHITECTURE one OF santaimen IS
BEGIN
PROCESS (CP,en)
BEGIN
IF en = '1' THEN
STM_OUT <= STM_IN;
ELSE
STM_OUT <= "ZZZZZZZZZZZZZZZZ";
END IF;
END PROCESS;
END;
18.模型机的实现
19.结果仿真图
仿真结果如上图所示,寄存器AC中存放着每一次相加的结果,实现1+2+3+...+10共加了9次,如上图所示,最后AC中存放的数(0000000000110111),就是十进制的和55,可严重结果计算正确。
20. 实验总结回顾
- 主存读不需要时钟,加上时钟会使某些部件延迟接收到数据。而写需要时钟。
- JNZ指令时,LoadPC发出后,PC代码中要有转移的代码,转移到Add指令。
- 微操作:例如LoadPC,说明可以向PC这个寄存器中存数,PC_bus,说明PC中的数据在此时钟下送入到总线,再供其他部件使用。
- 两个选择器:MUX_1:因为送入到AC中的数来源有两处,所以需要选择,SelA = “10”时,说明数据是从主线中来的,SelA = “01”时,说明是从ALU计算出来的。MUX_2:传入MDR的来源有两处:一是从主存来的,还有是最后将AC中的计算结果送入到主存中时,AC → 总线 →MDR → RAM。
- 三态门:有_bus的部件要使用三态门,帮助部件将数据送入到总线。
实验感受:
最大的感受就是:纸上得来终觉浅,绝知此事要躬行。自己亲自参与设计了这个实验的每一个部件,整体实验下来对整个课程的感悟肯定比只学知识而不做实验强多了,做了实验对知识有了更深的理解,方便更好的学习本门课程。