一、设计目的

系统掌握计算机的组成和工作原理,能够自助熟练准确地阐述计算机执行机器指令的工作过程,熟练应用并设计微指令、微程序的设计及调试。

二、设计内容

模型机与程序运行试验是一个综合性整机实验。该模型机包含7条机器指令,它能够依照用户执行微程序完成由加、与、非运算以及数据组合的任意复合运算。用户测试程序可以通过内存初始化的方式存储在内存中,也可以通过强迫写的方式循环写入内存。
这里我采用分模块整合法,此整机实验,由节拍脉冲、数据通路、微程序控制器、数码管显示4个模块组成。运算器、存储器、数据通路及微程序控制器中的时钟脉冲必须与时序电路相连。

1.以下为参考顶层电路:

模型 python 模型机_微程序控制


模型 python 模型机_机器指令_02

2.设计指令表如下:

模型 python 模型机_模型 python_03


这个实验主要通过以下三个步骤完成:

首先,将各个元件进行设计,并生成相应的bsf文件,注意这里最好对每一个元件进行单独的仿真验证和测试,然后通过仿真结果进行检测,看看是否正常运行。这里每一个元件的源程序都可以在书上找到。

其次,将各个元件进行连接,先进行线路上的链接,将相同的信号进行连接,然后对整个电路进行仿真验证。这里的链接方法在书上都有,就是那个顶层实体图,然后时钟信号根据微体系结构图得到

最后,根据仿真验证的结果,对整个电路进行微调,主要是对时钟信号进行调整。

三、详细设计

模型 python 模型机_机器指令_04

3.1设计的整体架构

总共大大小小有13个元件,其中有书上所指示的,也有我自己设计的(如ir),通过这11个元件相互之间的配合和时序上的整合,实现7个机器指令的实现(由于我实现的比较早,所以我在犹豫要不要实现其他的机器指令,如sub)。其中有多种信号。

1、jiepai元件:

用来产生t1~t4时钟节拍脉冲的元件;如图所示:

LIBRARY ieee;
USE ieee.std_logic_1164.all;

ENTITY jiepai IS
    PORT (
        clk : IN STD_LOGIC;
        tj : IN STD_LOGIC := '0';
        dp : IN STD_LOGIC := '0';
        qd : IN STD_LOGIC := '0';
        t1 : OUT STD_LOGIC;
        t2 : OUT STD_LOGIC;
        t3 : OUT STD_LOGIC;
        t4 : OUT STD_LOGIC;
        q1 : OUT STD_LOGIC;
        q2 : OUT STD_LOGIC;
        q3 : OUT STD_LOGIC
    );
END jiepai;

ARCHITECTURE BEHAVIOR OF jiepai IS
    TYPE type_fstate IS (idle,st1,s_st2,st4,st2,st3,s_st4,s_st3);
    SIGNAL fstate : type_fstate;
    SIGNAL reg_fstate : type_fstate;
    SIGNAL reg_t1 : STD_LOGIC := '0';
    SIGNAL reg_t2 : STD_LOGIC := '0';
    SIGNAL reg_t3 : STD_LOGIC := '0';
    SIGNAL reg_t4 : STD_LOGIC := '0';
BEGIN
    PROCESS (clk,reg_fstate)
    BEGIN
        IF (clk='1' AND clk'event) THEN
            fstate <= reg_fstate;
        END IF;
    END PROCESS;

    PROCESS (fstate,qd,dp,tj,reg_t1,reg_t2,reg_t3,reg_t4)
    BEGIN
            reg_t1 <= '0';
            reg_t2 <= '0';
            reg_t3 <= '0';
            reg_t4 <= '0';
            t1 <= '0';
            t2 <= '0';
            t3 <= '0';
            t4 <= '0';
            CASE fstate IS
                WHEN idle =>
                    IF (NOT((qd = '1'))) THEN
                        reg_fstate <= st1;
                    ELSE
                        reg_fstate <= idle;
                    END IF;
                    reg_t4 <= '0';
                    reg_t3 <= '0';
                    reg_t2 <= '0';
                    reg_t1 <= '0';
                WHEN st1 =>
                    IF (((tj = '1') AND NOT((dp = '1')))) THEN
                        reg_fstate <= st1;
                    ELSIF (((dp = '1') AND NOT((tj = '1')))) THEN
                        reg_fstate <= s_st2;
                    ELSE
                        reg_fstate <= st2;
                    END IF;
                    reg_t4 <= '0';
                    reg_t3 <= '0';
                    reg_t2 <= '0';
                    reg_t1 <= '1';
                WHEN s_st2 =>
                    IF ((tj = '1')) THEN
                        reg_fstate <= s_st2;
                    ELSE
                        reg_fstate <= s_st3;
                    END IF;
                    reg_t4 <= '0';
                    reg_t3 <= '0';
                    reg_t2 <= '1';
                    reg_t1 <= '0';
                WHEN st4 =>
                    IF (((tj = '1') AND NOT((dp = '1')))) THEN
                        reg_fstate <= st4;
                    ELSIF (((dp = '1') AND NOT((tj = '1')))) THEN
                        reg_fstate <= idle;
                    ELSE
                        reg_fstate <= st1;
                    END IF;
                    reg_t4 <= '1';
                    reg_t3 <= '0';
                    reg_t2 <= '0';
                    reg_t1 <= '0';
                WHEN st2 =>
                    IF (((tj = '1') AND NOT((dp = '1')))) THEN
                        reg_fstate <= st2;
                    ELSIF (((dp = '1') AND NOT((tj = '1')))) THEN
                        reg_fstate <= s_st3;
                    ELSE
                        reg_fstate <= st3;
                    END IF;
                    reg_t4 <= '0';
                    reg_t3 <= '0';
                    reg_t2 <= '1';
                    reg_t1 <= '0';
                WHEN st3 =>
                    IF (((tj = '1') AND NOT((dp = '1')))) THEN
                        reg_fstate <= st3;
                    ELSIF (((dp = '1') AND NOT((tj = '1')))) THEN
                        reg_fstate <= s_st4;
                    ELSE
                        reg_fstate <= st4;
                    END IF;
                    reg_t4 <= '0';
                    reg_t3 <= '1';
                    reg_t2 <= '0';
                    reg_t1 <= '0';
                WHEN s_st4 =>
                    IF ((tj = '1')) THEN
                        reg_fstate <= s_st4;
                    ELSE
                        reg_fstate <= idle;
                    END IF;
                    reg_t4 <= '1';
                    reg_t3 <= '0';
                    reg_t2 <= '0';
                    reg_t1 <= '0';
                WHEN s_st3 =>
                    IF ((tj = '1')) THEN
                        reg_fstate <= s_st3;
                    ELSE
                        reg_fstate <= s_st4;
                    END IF;
                    reg_t4 <= '0';
                    reg_t3 <= '1';
                    reg_t2 <= '0';
                    reg_t1 <= '0';
                WHEN OTHERS => 
                    reg_t1 <= 'X';
                    reg_t2 <= 'X';
                    reg_t3 <= 'X';
                    reg_t4 <= 'X';
                    report "Reach undefined state";
            END CASE;
            t1 <= reg_t1;
            t2 <= reg_t2;
            t3 <= reg_t3;
            t4 <= reg_t4;
    END PROCESS;
END BEHAVIOR;

这是第五次实验——时序电路实验的内容,如果能够正确完整的完成第五次实验,那么这个就不是什么难题。第五次实验教会了我们一种使用quartus软件画状态图并生成vhdl的方法。这个元件主要是生成四个时钟脉冲信号,通过这四个时钟脉冲信号控制整个电路。

2、kongzhi元件:

整个控制器的元件,其中包含了rom(用来产生相应的微指令信号)元件和pp(我自己设置的,用来控制下址)元件;如图所示:

模型 python 模型机_微程序控制_05


pp元件的vhdl为:

library ieee;
use ieee.std_logic_1164.all;
entity pp is
port(clock:in std_logic;
	p:in std_logic;
	ir7:in std_logic;
	ir6:in std_logic;
	ir5:in std_logic;
	a5:in std_logic;
	a4:in std_logic;
	a3:in std_logic;
	a2:in std_logic;
	a1:in std_logic;
	rd:in std_logic;
	we:in std_logic;
	o5:out std_logic;
	o4:out std_logic;
	o3:out std_logic;
	o2:out std_logic;
	o1:out std_logic
	);
end pp;
architecture rtl of pp is
signal aa,bb:std_logic;
begin 
process(clock,p,ir7,ir6,ir5,rd,we)
begin
	if(rd='0') then
		o5<=a5;
		o4<=a4;
		o3<=a3;
		o2<=a2;
		o1<=a1;
	elsif(we='0') then
		o5<='1';
		o4<='0';
		o3<='0';
		o2<='0';
		o1<='0';
	else
		if(p='0') then
			o5<=a5;
			o4<=a4;
			o3<=a3;
			o2<=a2;
			o1<=a1;
		elsif(ir7='0' and ir6='0' and ir5='0')then
			o5<=a5;
			o4<=a4;
			o3<=a3;
			o2<=a2;
			o1<=a1;	
		else
			o5<='0';
			o4<='1';
			o3<=ir7;
			o2<=ir6;
			o1<=ir5;
		end if;
	end if;
end process;
end rtl;

rom元件的vhdl为:

LIBRARY ieee;			
USE ieee.std_logic_1164.all;			
ENTITY rom IS			
PORT			
(a4,a3,a2,a1,a0:in std_logic;
	d1,d2,d3,d4,d5,d6,d7,d8,d9,d10,d11,d12,d13,d14,d15,d16,d17,d18,d19,d20,d21,d22,d23,d24,d25,d26,d27,d28:out std_logic			
     --address  : IN     STD_LOGIC_VECTOR (4 DOWNTO 0);			
     --          q  : OUT STD_LOGIC_VECTOR (27 DOWNTO 0)
);			
END rom;			
ARCHITECTURE SYN OF rom IS		
signal address:std_logic_vector(4 downto 0);
SIGNAL sub_wire0 : STD_LOGIC_VECTOR (27 DOWNTO 0);				
BEGIN			
address<=a4&a3&a2&a1&a0;
sub_wire0<=			
"1010111100000001000000000001"	 WHEN address= 	"00000"	ELSE
"1111111000001001000000000010"	 WHEN address= 	"00001"	ELSE
"1001111100000101000001001000"	 WHEN address= 	"00010"	ELSE
"1001111100000001000000110011"	 WHEN address= 	"01000"	ELSE
"1111111000001001000000010101"	 WHEN address= 	"01001"	ELSE
"1111111000001001000000010111"	 WHEN address= 	"01010"	ELSE
"1111111000001001000000011001"	 WHEN address= 	"01011"	ELSE
"1001101100010001000000011011"	 WHEN address= 	"01100"	ELSE
"1111111000001001000000011100"	 WHEN address= 	"01101"	ELSE
"1111111000001001000000000011"	 WHEN address= 	"01110"	ELSE
"1111111000001001000000011101"	 WHEN address= 	"01111"	ELSE
"1001111100001001000001010110"	 WHEN address= 	"10101"	ELSE
"1001111110000001000001000001"	 WHEN address= 	"10110"	ELSE
"1001111100001001000001011000"	 WHEN address= 	"10111"	ELSE
"1001101100000001000010000001"	 WHEN address= 	"11000"	ELSE
"1001111100001001000001011010"	 WHEN address= 	"11001"	ELSE
"1000111100000001000001000001"	 WHEN address= 	"11010"	ELSE
"1001110110000010000000000001"	 WHEN address= 	"11011"	ELSE
"1011111100000001000001000001"	 WHEN address= 	"11100"	ELSE
"1001111100001001000001000100"	 WHEN address= 	"00011"	ELSE
"1001111100100001000001000101"	 WHEN address= 	"00100"	ELSE
"1001101100010001000000000110"	 WHEN address= 	"00101"	ELSE
"1001110110000001100100000001"	 WHEN address= 	"00110"	ELSE
"1001111100001001000001011110"	 WHEN address= 	"11101"	ELSE
"1001111100100001000001011111"	 WHEN address= 	"11110"	ELSE
"1001101100010001000000000111"	 WHEN address= 	"11111"	ELSE
"1001110110000000101100000001"	 WHEN address= 	"00111"	ELSE
"1010111100000001000000010001"	 WHEN address= 	"10000"	ELSE
"1111111000001001000000010010"	 WHEN address= 	"10001"	ELSE
"1111111000001001000000010100"	 WHEN address= 	"10011"	ELSE
"1000111100000001000010010001"	 WHEN address= 	"10010"	ELSE
"1001111100000001000001010011"	;		
d1<=sub_wire0(27);
d2<=sub_wire0(26);
d3<=sub_wire0(25);
d4<=sub_wire0(24);
d5<=sub_wire0(23);
d6<=sub_wire0(22);
d7<=sub_wire0(21);
d8<=sub_wire0(20);
d9<=sub_wire0(19);
d10<=sub_wire0(18);
d11<=sub_wire0(17);
d12<=sub_wire0(16);
d13<=sub_wire0(15);
d14<=sub_wire0(14);
d15<=sub_wire0(13);
d16<=sub_wire0(12);
d17<=sub_wire0(11);
d18<=sub_wire0(10);
d19<=sub_wire0(9);
d20<=sub_wire0(8);
d21<=sub_wire0(7);		
d22<=sub_wire0(6);
d23<=sub_wire0(5);
d24<=sub_wire0(4);
d25<=sub_wire0(3);
d26<=sub_wire0(2);
d27<=sub_wire0(1);
d28<=sub_wire0(0);
END SYN;

这是第六次实验的内容,主要在于rom元件的设计和信号的安排。可以直接拿来用。

3、shutong元件

数据通路元件,其中包含了exp_r_alu(alu运算器模块,用来进行数据运算)元件和exp_ram_vhd(其中包含了ram寄存器模块和pc、ar寄存器模块,用来访问内存文件,产生指令,产生计数,存储指令和地址)元件;

模型 python 模型机_微程序控制_06


exp_r_alu元件的vhdl为:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity exp_r_alu is
port( clk1:in std_logic;--t3
	clk2:in std_logic;--t2
	sw_bus,r4_bus,r5_bus,alu_bus:in std_logic;
	lddr1,lddr2,ldr4,ldr5:in std_logic;
	m,cn:in std_logic;
	s:in std_logic_vector(3 downto 0);
	k:in std_logic_vector(7 downto 0):="00000000";
	d:inout std_logic_vector(7 downto 0);
	--ou:out std_logic_vector(7 downto 0);
	rr5:out std_logic_vector(7 downto 0);
	rr4:out std_logic_vector(7 downto 0);
	rr1:out std_logic_vector(7 downto 0);
	rr2:out std_logic_vector(7 downto 0));
end exp_r_alu;
architecture rtl of exp_r_alu is
signal dr1,dr2,r4,r5,aluout,bus_reg:std_logic_vector(7 downto 0);
signal sel:std_logic_vector(5 downto 0);
begin
ldreg:process(clk1,clk2,lddr1,lddr2,ldr4,ldr5,bus_reg)
	begin
		if clk1'event and clk1='1' then
			if ldr4='1' then r4<=bus_reg;
			elsif ldr5='1' then r5<=bus_reg;
			end if;
		end if;
		if clk2'event and clk2='1' then
			if lddr1='1' then dr1<=bus_reg;
			elsif lddr2='1' then dr2<=bus_reg;
			end if;
		end if;
	end process;
alu:process(m,cn,s,dr1,dr2,sel,aluout)
	begin
		sel<=m&cn&s;
		case sel is
			when "000000"=>aluout<=dr1+1;
			when "010000"=>aluout<=dr1;
			when "100000"=>aluout<=not dr1;
			when "000001"=>aluout<=(dr1 or dr2)+1;
			when "010001"=>aluout<=dr1 or dr2;
			when "100001"=>aluout<=not(dr1 or dr2);
			when "000010"=>aluout<=(dr1 or (not dr2))+1;
			when "010010"=>aluout<=(dr1 or (not dr2));
			when "100010"=>aluout<=(not dr1)and dr2;
			when "000011"=>aluout<=x"00";
			when "010011"=>aluout<=aluout-1;
			when "100011"=>aluout<=x"00";
			when "000100"=>aluout<=dr1+(dr1 and (not dr2))+1;
			when "010100"=>aluout<=dr1+(dr1 and (not dr2));
			when "100100"=>aluout<=not(dr1 and dr2);
			when "000101"=>aluout<=(dr1 or dr2)or(dr1 and dr2)or x"01";
			when "010101"=>aluout<=(dr1 or dr2)+(dr1 and (not dr2));
			when "100101"=>aluout<=not dr2;
			when "000110"=>aluout<=dr1-dr2;
			when "010110"=>aluout<=dr1-dr2-1;
			when "100110"=>aluout<=dr1 xor dr2;
			when "000111"=>aluout<=dr1 and (not dr2);
			when "010111"=>aluout<=(dr1 and (not dr2))-1;
			when "100111"=>aluout<=dr1 and(not dr2); 
			when "001000" => aluout<=dr1 + (dr1 and dr2)+1;
			when "011000" => aluout<=dr1 + (dr1 and dr2);
			when "101000" => aluout<= (not dr1) or dr2;
			when "001001" => aluout<=dr1 + dr2 +1;
			when "011001" => aluout<=dr1 + dr2;
			when "101001" => aluout<=(dr1 xnor dr2);
			when "001010" => aluout<=(dr1 or(not dr2))+(dr1 and dr2)+1;
			when "011010" => aluout<=(dr1 or(not dr2))+(dr1 and dr2);
			when "101010" => aluout<=dr2;
			when "001011" => aluout<=dr1 and dr2;
			when "011011" => aluout<=(dr1 and dr2)-1;
			when "101011" => aluout<=(dr1 and dr2);
			when "001100" => aluout<=dr1 + dr1 +1;
			when "011100" => aluout<=(dr1 or dr1); 
			when "101100" => aluout<=x"01";
			when "001101" => aluout<=(dr1 or dr2)+dr1+1;
			when "011101" => aluout<=(dr1 or dr2)+dr1;
			when "101101" => aluout<=dr1 or(not dr2);
			when "001110" => aluout<=(dr1 or (not dr2))+dr1+1;
			when "011110" => aluout<=(dr1 or (not dr2))+dr1;
			when "101110" => aluout<=dr1 or dr2;
			when "001111" => aluout<=dr1;
			when "011111" => aluout<=dr1-1;
			when "101111" => aluout<=dr1;
			when others =>aluout<=x"ff";
			end case;
		end process;
		bus_Reg<=k      when(sw_bus='0' and r4_bus='1' and r5_bus='1' and ALU_bus='1') else
				r4      when(sw_bus='1' and r4_bus='0' and r5_bus='1' and ALU_bus='1') else
				r5      when(sw_bus='1' and r4_bus='1' and r5_bus='0' and ALU_bus='1') else
				aluout  when(sw_bus='1' and r4_bus='1' and r5_bus='1' and ALU_bus='0') else 
				d;
		d<=bus_Reg when(sw_bus='0' or r4_bus='0' or r5_bus='0' or ALU_bus='0') else--******
		(others=>'Z');
		rr5<=r5;
		rr4<=r4;
		rr1<=dr1;
		rr2<=dr2;
end rtl;

exp_ram_vhd元件为:

模型 python 模型机_时钟脉冲_07


其中的sw_pc_ar元件的vhdl为:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity sw_pc_ar is
port(clk_cdu,pcclr,pcld,pcen:in std_logic;--Z,1,0,0
	sw_bus,pc_bus,ldar:in std_logic;--1,1,0
	inputd:in std_logic_vector(7 downto 0):="00000000";
	arout:out std_logic_vector(7 downto 0);
	d:inout std_logic_vector(7 downto 0);
	pco:out std_logic_vector(7 downto 0));
end sw_pc_ar;
architecture rtl of sw_pc_ar is
signal pc,ar,bus_reg:std_logic_vector(7 downto 0):="00000000";
begin
seql:process(clk_cdu,ldar,bus_reg)--when ldar==1 let bus into ar
	begin
		if clk_cdu'event and clk_cdu='1' then
			if ldar='1' then
				ar<=bus_reg;
			end if;
		end if;
	end process;
sep2:process(clk_cdu,pcclr,pcld,pcen,bus_reg)
	begin
		if pcclr='0' then--when pcclr==0 clear pc
			pc<=(others=>'0');
		elsif clk_cdu'event and clk_cdu='1' then
			if (pcld='0' and pcen='1') then--when pcen==1 & pcld==0 let bue into pc
				pc<=bus_reg;
			elsif (pcld='1' and pcen='1') then--when pcen==1 & pcld==1 pc++
				pc<=pc+1;
			end if;
		end if;
	end process;
bus_reg<=inputd when (sw_bus='0' and pc_bus='1') else--when pc_bus==1 let inputd in bus
	pc when (sw_bus='1' and pc_bus='0') else--when sw_bus==1 let pc in bus
	d;--when 0 0 let d in bus
d<=bus_reg when (sw_bus='0' or pc_bus='0') else--when 0 0 let bus into d
	(others=>'Z');--when else let Z into d
arout<=ar;--show ar
pco<=pc;
end rtl;

这是第四次实验的内容,已经将总线和alu进行了整合,十分的简单和方便。如果能够完整的完成第四次实验的仿真和下载,那么并不是什么难事。

4、xianshi元件:

显示模块并不是实验中要求的,而更多的是我们自己学习的,是附加电路的内容,由于附加电路是我自己完全独立设计的,没有参考书上的什么内容,所以我比较熟悉,也比较清楚它的各个端口是做什么的,有什么样的作用,再加上我曾经做的跑马灯,对于八个数码管的同时显示也比较熟悉,所以总的来说,设计出来并不是太难。
显示数码管元件,其中包含了seg(用来控制段选信号的模块)元件、cpu16(用来产生计数的模块)元件和sanba(用来控制位选信号的模块)元件,下文会进行详细的介绍。
在所有的信号中,clk是执行模块(包括控制器和数据通路)的时钟输入信号,clock是显示模块的时钟输入信号,clr、krd、kwe是控制器的控制输入信号,tj、dp、qd是节拍(时序电路)的控制输入信号,k是数据输入信号,其他所有的信号均为输出信号,用来检测是否运行正确以及下载后控制数码管显示数据。注意寄存器的初值已经在编写的时候输入到了寄存器的mif文件中,在后续的演示中除了想要增加,不需要人为输入。
在检测的时候主要观察ir7~ir6信号(指令信号),p信号(总使能信号),rr5信号(r5寄存器数据输出信号),pc信号(计数器数据输出信号),bus信号(总线数据输出信号),ao信号(ar寄存器数据输出信号)。
运行的时候以每4个时钟脉冲周期为一个微指令周期,多个微指令周期为一个机器指令周期,实现一个机器指令。
在这里,我是严格按照书上的要求设计的时钟信号,只是在显示模块的时候加入了一个新的时钟信号,用来控制持续显示而不是频闪。

3.2附加电路说明

模型 python 模型机_时钟脉冲_08

1、seg模块:

用来产生段选信号的,通过输入的数据来产生相应的段选信号,用来控制在数码管上的显示来输出数据,vhdl如下:

library ieee;
use ieee.std_logic_1164.all;
entity seg is
port(input:in std_logic_vector(3 downto 0);
	en:in std_logic;
	output:out std_logic_vector(7 downto 0));
end seg;
architecture ru of seg is
begin
process(input,en)
begin
if en='1' then	
	if input="0000" then
		output<="11111100";
	elsif input="0001" then
		output<="01100000";
	elsif input="0010" then
		output<="11011010";
	elsif input="0011" then
		output<="11110010";
	elsif input="0100" then
		output<="01100110";
	elsif input="0101" then
		output<="10110110";
	elsif input="0110" then
		output<="10111110";
	elsif input="0111" then
		output<="11100000";
	elsif input="1000" then
		output<="11111110";
	elsif input="1001" then
		output<="11110110";
	elsif input="1010" then
		output<="11101110";
	elsif input="1011" then
		output<="00111110";
	elsif input="1100" then
		output<="10011100";
	elsif input="1101" then
		output<="01111010";
	elsif input="1110" then
		output<="10011110";
	elsif input="1111" then
		output<="10001110";	
	end if;
else output<="00000010";
end if;
end process;
end ru;

当en为1的时候可以输出数据相应的段选信号,当en为0的时候将会输出一条杠

2、cpu16元件:

用来产生计数的,用来控制哪个数码管在哪个时候进行显示,具体代码如下:

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY cpu16 IS
PORT(CLK,RST,EN : IN STD_LOGIC;
	CQ : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
	COUT : OUT STD_LOGIC);
END ENTITY cpu16;
ARCHITECTURE behav OF cpu16 IS
BEGIN
PROCESS (CLK,RST,EN)
	VARIABLE CQI : STD_LOGIC_VECTOR(3 DOWNTO 0);
	BEGIN
	IF RST='1' THEN CQI:=(OTHERS =>'0');
	ELSIF CLK'EVENT AND CLK='1' THEN
		IF EN='1' THEN 
			IF CQI<7 THEN CQI:=CQI+1;
			ELSE CQI := (OTHERS =>'0');
			END IF;
		END IF;
	END IF;
	IF CQI=2 THEN COUT<='0';
	elsif cqi=5 then cout<='0';
	ELSE COUT<='1';
	END IF;
	CQ<=CQI;
END PROCESS;
END ARCHITECTURE behav;

将会进行0~7的数字循环,每个数字对应一个数码管,实际上就是一个简单的数字计数器罢了。

3、sanba译码器元件:

用来产生位选信号,用来配合cput16控制哪个数码管在哪个时间进行显示,vhdl如下:

library ieee;
use ieee.std_logic_1164.all;
entity sanba is
port(input:in std_logic_vector(2 downto 0);
	o0:out std_logic;
	o1:out std_logic;
	o2:out std_logic;
	o3:out std_logic;
	o4:out std_logic;
	o5:out std_logic;
	o6:out std_logic;
	o7:out std_logic);
end sanba;
architecture ru of sanba is
signal output:std_logic_vector(7 downto 0);
begin
process(input)
begin
if input="000" then
	output<="00000001";
elsif input="001" then
	output<="00000010";
elsif input="010" then
	output<="00000100";
elsif input="011" then
	output<="00001000";
elsif input="100" then
	output<="00010000";
elsif input="101" then
	output<="00100000";
elsif input="110" then
	output<="01000000";
elsif input="111" then
	output<="10000000";
end if;
end process;
o0<=not output(7);
o1<=not output(6);
o2<=not output(5);
o3<=not output(4);
o4<=not output(3);
o5<=not output(2);
o6<=not output(1);
o7<=not output(0);
end ru;

将会循环使8位数码管进行显示

3.3仿真结果说明

模型 python 模型机_时钟脉冲_09


模型 python 模型机_微程序控制_10


具体分析如下:

115ns~285ns执行的是LDA指令,将03H给r5寄存器

先进行01001指令,将pc中的数值给ar寄存器,pc再加1,此时ar的值为01H

再进行10101指令,将ram中ar位置的地址的数值取出来,再给ar寄存器,此时ram取出来的数值是14H,ar寄存器的数值即为14H

再进行10110指令,将ram中ar位置的地址的数值取出来,再给r5寄存器,此时ram取出来的数值是03H ,r5寄存器的数值即为03H

285ns~475ns执行的是COM指令,将03H取反得到FCH再赋值给r5寄存器

先进行01100指令,将r5寄存器的数值放到dr1暂存器中,此时dr1暂存器的数值为03H

再进行11011指令,将dr1中的数值进行取反操作后再赋值给r5寄存器,此时r5寄存器的数值为FCH

475ns~795ns执行的是ADD指令,相加后结果为FDH在赋值给r5寄存器

先进行01110指令,将pc中的数值给ar寄存器,pc再加1,此时ar的值为04H

再进行00011指令,将ram中ar位置的地址的数值取出来,再给ar寄存器,此时ran取出来的数值是12H,ar寄存器的数值即为12H

再进行00100指令,将ram中ar位置的地址的数值取出来,再给dr2暂存器,此时ram取出来的数值是01H,dr2寄存器的数值即为01H

再进行00101指令,将r5寄存器的数值给dr1暂存器,此时dr1暂存器的数值为FCH

再进行00110指令,将dr1和dr2的数值进行相加,再将结果给r5寄存器,此时r5寄存器的数值为FDH

795ns~1045ns执行的是STA指令,将r5寄存器中的值放到ram中

先进行01010指令,将pc中的数值给ar寄存器,pc再加1,此时ar的数值为06H

再进行10111指令,将ram中ar位置的地址的数值取出来,再给ar寄存器,此时ram取出来的数值是16H,ar寄存器的数值即为16H

再进行11000指令,将r5寄存器的数值放到ram中ar位置的地址

1045ns~1275ns执行的是LDA指令,将02H给r5寄存器

先进行01001指令,将pc中的数值给ar寄存器,pc再加1,此时ar的值为08H

再进行10101指令,将ram中ar位置的地址的数值取出来,再给ar寄存器,此时ram取出来的数值是13H,ar寄存器的数值即为02H

再进行10110指令,将ram中ar位置的地址的数值取出来,再给r5寄存器,此时ram取出来的数值是02H,r5寄存器的数值即为02H

1275ns~1595ns执行的是ADD指令,相加后结果为06H再赋值给r5寄存器

先进行01110指令,将pc中的数值给ar寄存器,pc再加1,此时ar的值为0AH

再进行00011指令,将ram中ar位置的地址的数值取出来,再给ar寄存器,此时ran取出来的数值是15H,ar寄存器的数值即为15H

再进行00100指令,将ram中ar位置的地址的数值取出来,再给dr2暂存器,此时ram取出来的数值是04H,dr2寄存器的数值即为04H

再进行00101指令,将r5寄存器的数值给dr1暂存器,此时dr1暂存器的数值为02H

再进行00110指令,将dr1和dr2的数值进行相加,再将结果给r5寄存器,此时r5寄存器的数值为06H

1595ns~1915ns执行的是AND指令,相与后结果为04H再赋值给r5寄存器

先进行01110指令,将pc中的数值给ar寄存器,pc再加1,此时ar的值为0CH

再进行11101指令,将ram中ar位置的地址的数值取出来,再给ar寄存器,此时ram取出来的数值是16H,ar寄存器的数值即为16H

再进行11110指令,将ram中ar位置的地址的数值取出来,再给dr2暂存器,此时ram取出来的数值是FDH,dr2暂存器的数值即为FDH

再进行11111指令,将r5寄存器的数值给dr1暂存器,此时dr1暂存器的数值即为06H

再进行00111指令,将dr1和dr2的数值进行相与,再将结果给r5寄存器,此时r5寄存器的数值为04H

1915ns~2115ns执行的是COM指令,将04H取反得到FBH再赋值给r5寄存器

先进行01100指令,将r5寄存器的数值放到dr1暂存器中,此时dr1暂存器的数值为04H

再进行11011指令,将dr1中的数值进行取反操作后再赋值给r5寄存器,此时r5寄存器的数值为FBH

2115ns~2365ns执行的是OUT指令,将存储器16H位置的值展示到bus上

先进行01011指令,将pc中的数值给ar寄存器,pc再加1,此时ar的数值为0FH

再进行11001指令,将ram中ar位置的地址的数值取出来,再给ar寄存器,此时ram取出来的数值是16H

再进行11010指令,将ram中ar位置的地址的数值取出来,再发送到总线,此时总线上的数值为FDH

2365ns~2565ns执行的是JMP指令,跳转回00H位置的指令重复执行

先进行01101指令,将pc中的数值给ar寄存器,pc再加1,此时ar的值为11H

再进行11100指令,将ram中ar位置的地址的数值取出来,再给pc计数器,此时ram取出来的数值是00H,pc计数器的数值即为00H

3.4mif文件展示

模型 python 模型机_微程序控制_11

3.5报告

1)假如控制器中模拟指令码的ir7ir6ir5对应到数据总线d[7…0]的d4d6d1,译码出7条机器指令的指令码为:

机器指令

指令码

二进制指令码

十六进制指令码

LDA

001

00000001

01H

STA

010

01000000

40H

ADD

110

01010000

50H

AND

111

01010001

51H

COM

100

00010000

10H

OUT

011

01000001

41H

JMP

101

00010001

11H

2)用模型机已有的7条机器指令编写测试程序,实现复合运算:

not((not(c))加a)and(b加d))
其中a=01H,b=02H,c=03H,d=04H
先将数据c放入r5寄存器中,再进行not(c),将结果放在r5寄存器中,
再进行not(c)加a,将结果放在r5寄存器中,将结果放到存储器中,
再将数据b放入r5寄存器中,再进行(b加d),结果放到r5寄存器中,
再将存储器中存储的not(c)加a取出,与r5进行相与,将结果放到r5寄存器中,
再对r5寄存器进行取反操作,结果放到r5寄存器中,
此时r5寄存器中的值就是我们要进行的复合运算的最终值

具体的代码表如下:

RAM地址

内容

说明

00H

20H

LDA双字节指令

01H

14H

LDA 14将地址14H中内容送到r5(r5=03H)

02H

80H

COM单字节指令,将r5取反送到r5(r5=FCH)

03H

C0H

ADD双字节指令

04H

12H

ADD 12将地址12H中的内容与r5相加送到r5(r5=FDH)

05H

40H

STA双字节指令

06H

16H

STA 16将r5的值放到地址16H的单元

07H

20H

LDA双字节指令

08H

13H

LDA 13将地址13H中内容送到r5(r5=02H)

09H

C0H

ADD双字节指令

0AH

15H

ADD 15将地址15H中的内容与r5相加送到r5(r5=06H)

0BH

E0H

AND双字节指令

0CH

16H

AND 16将地址16H中的内容与r5相与送到r5(r5=04H)

0DH

80H

COM单字节指令,将r5取反送到r5(r5=FBH)

0EH

60H

OUT双字节指令

0FH

16H

OUT 16将地址16H中的内容送到BUS

10H

A0H

JMP双字节指令

11H

00H

JMP 00无条件转移到00H地址

12H

01H

数据a

13H

02H

数据b

14H

03H

数据c

15H

04H

数据d

16H

00H

数据暂存

a,b,c,d的值存储在12H~15H的位置

mif文件为:

模型 python 模型机_时钟脉冲_12

下载图示:

1、针脚设置:

模型 python 模型机_时钟脉冲_13


芯片还是选取ep5t144c8的芯片,因为我们的芯片就是这个,仅支持这个

将qd模型机启动控制信号,clr清零控制信号,kwe强读控制信号和krd强写控制信号放在了按钮开关,因为他们的初始值是1;

将k八位输入数据,tj停机信号,dp单拍进行控制信号放在了拨码开关,因为他们的初始值是0,而且这样可以方便我进行数值设置。

通过数码管显示ar寄存器,bus总线和r5寄存器的数值。

通过二极管101-104来显示t1-t4四个节拍脉冲信号。二极管93,92,87分别代表ir7,ir6,ir5,为ir寄存器输出的数值。

给模型机的时钟信号是91时钟信号,频率为100hz,便于观测;

给显示数码管的时钟信号是17时钟信号,以此来消除频闪。

针脚设计好之后点击全编译,生成sof文件。

通过programer工具将sof文件加在到板子上。

2、下载结果:

指令

图示(由左到右依次是ar-bus-r5)

初始状态


LDA转移指令,r5=03H


COM取反指令,r5=FCH


ADD相加指令,r5=FDH


STA存储指令,bus=FDH


LDA转移指令,r5=02H


ADD相加指令。r5=06H


AND相与指令,r5=04H


COM取反指令,r5=FBH


OUT输出指令,bus=00H


JMP跳转指令,ar=00H


由此可以看出,在JMP指令执行后,r5寄存器中的值就是我们所要的复合运算的值FB。

3)回答问题

a、代码中进程ct1、ct2、ct3、ct4功能划分的依据是
ct1的功能是微序列控制器下址跳转
ct2的功能是实现各种指令,主要集中在实现从存储器或寄存器释放数据到总线上
ct3的功能是完成各种指令,从总线上装载数据到相应的存储器或寄存器中
ct4的功能是生成下址

b、代码中如何定义并初始化RAM
通过定义一个名叫ram8的中间变量进行定义,并通过赋初值来进行初始化:
signalram8:RAM:=(x”20”,x”0d”,x”c0”,x”0e”,x”40”,x”10”,x”60”,x”10”,x”e0”,x”0f”,x”80”,x”a0”,x”00”,x”55”,x”8a”,x”f0”,x”ff”,others=>x”00”)
其中括号中的数据是放入的初始值,即原本mif文件中的数据

c、代码中bus_reg_t2<=ram8(conv_integer(ar))与ram8(conv_integer(ar))<=r5的含义是什么
第一句是将ram中ar地址位置的数据值传送到总线上
第二句是将r5寄存器中的数据值传送到ram中ar地址的位置

4)vhdl中如何考虑多个时钟信号的情况

由于vhdl中是通过process进程进行设计的,所以只需要在进程中对不同的时钟信号进行条件选择,就可以在不同的时钟信号执行不同的操作。
但是注意要考虑到时序上的整合问题。
比方说,本次试验的时候,可以有四个不同的时钟信号,但是一定要满足:
在最先到达的一个时钟的波峰进行微序列控制器的下址跳转;
在第二个到达的波峰进行取数据的功能,即将数据从寄存器或存储器中取出来放到总线上,例如从ram中取出运算数据发送到总线上;
在第三个到达的波峰进行存数据的功能,即将数据从总线上存到寄存器或存储器中,例如将总线上的运算结果存回到r5寄存器中;
在最后一个到达的波峰进行下一个下址的生成。
虽然我是通过bdf和bsf的元件拼接整合实现的,但在我的整合过程中也注意了时序上的整合问题,比方说我给当前下址寄存器的时钟信号是t2时序脉冲,给寄存器和存储器的时钟信号一般是t3时序脉冲,给pp(我自己设计的下一下址寄存器)元件的时钟信号是t4时序脉冲,通过这样才达到了整个电路的正常运行。
当然,时序上的整合顺序不只有这一种,可以通过不同的设计方式进行,比方说我的舍友就将p1信号进行了提前,这样他就会提前进行判断是否进行强读强写,然后他又将所有的时钟信号进行了一个延迟,也做到了模拟机的正常运行。
所以我认为,时序上的整合方式不只有一种,我是按照书上的整合方式来的,并没有做太多的改动,但我也不认为只有我这种是正确的,毕竟这是一个开放性的大综合实验,只要能够设计出来,能够正常运行,就是可以的。

四、优化

因为我们可以有多个输出,想要查看多个值,如r5寄存器,r1暂存器,r2暂存器,pc计数器,ar寄存器,bus总线等等,而我们的数码管只有八个,只能显示3组数据,所以有些不够用,而我在我的搭档俞阳博的启发下,想到了一种可以多个显示的方法,即通过拨码开关来控制显示哪个数据,哪个寄存器,如下:

我定义了拨码开关52和51两个输入来控制r5,r1,r2,pc显示哪一个寄存器或计数器的数值,当52~51=00的时候(初始状态),第三组数码管显示r5寄存器的数值;当=01的时候显示r1暂存器的数值;当=10的时候显示r2暂存器的数值;当=11的时候显示pc计数器的数值。具体代码如下:

模型 python 模型机_时钟脉冲_14

library ieee;
use ieee.std_logic_1164.all;
entity tf is
port(
	kg:in std_logic_vector(1 downto 0);
	m1:in std_logic_vector(7 downto 0);
	m2:in std_logic_vector(7 downto 0);
	m3:in std_logic_vector(7 downto 0);
	m4:in std_logic_vector(7 downto 0);
	mo:out std_logic_vector(7 downto 0)
	);
end tf;
architecture rtl of tf is
begin 
cin:process(kg)
begin
	if kg="00" then
		mo<=m1;
	elsif kg="01" then
		mo<=m2;
	elsif kg="10" then
		mo<=m3;
	else
		mo<=m4;
	end if;
end process;
end rtl;

实际上只不过是一个二四译码器的变形,根据输入的kg控制信号,来选择输出哪个八位的二进制数据,然后将输出的八位的二进制数据连接到显示模块的第三个输入数据,就可以通过52~51拨码开关进行控制要输出什么数据。
下载结果如下:

拨码开关(52~51)

数码管(ar-bus-r5/r1/r2/pc)

00(r5)


01(r1)


10(r2)


11(pc)


由此可以看出,当ar=03,bus=04,r5=FC的时候,执行的应该是COM取反指令,此时的r1暂存器的值是03(保持上一指令的数据,并没有用到),r2暂存器的值是00(并没有用到),pc计数器的值是04(即将执行下一指令)。结果正确。
由此可以看出,这么设置之后整个实验的观测简单了很多,也方便了很多,在这里特别感谢我的搭档俞阳博提出的这么一个想法,我之前自己并没有想到。

五、总结

作为计算机系统原理实验第一部分的最后一个大综合实验,本次实验难度系数高,实验复杂,涉及了开学五个周以来所学习的所有的知识点,而且不仅仅是简单的数据规模以及模块元件上的整合,更重要的是时钟脉冲信号上的整合,通过对t1~t4这四个时钟节拍的合理调用,来调度整个电路的正常进行。
总的来说,在昨晚之前的六个实验之后,模拟机各个组件的实现已经不再是十分困难的存在了,困难的是如何将所有的原件进行组织上和时序上的整合,一旦有什么不对,结果就很容易出差错。我基本上是通过书上的大部分内容进行设计的,在最后加上了少部分自己的一些元件用来确保整个电路的正常进行。
模拟机这个实验设计是我从第四周周二就开始了,一直写了差不多一个周才算基本完成,后来因为强读和强写操作并不是很容易写出来,所以又进行了大量的修改,最终是在第五周的周四全部实现了。
实际上,这次我感觉还是比较简单的,和上学期写CPU时我写了六个CPU才得到一个我比较满意的版本相比,我这次总共就写了两个就基本上实现了,因为做模拟机有书进行对照,所以并不是十分困难。但是不可否认,这次大综合实验非常的繁琐,虽然有了书的指导之后,不再像上学期那样全靠自己摸索,而是终于有迹可循,不过书上的内容也不能全信,我的第一个版本就是因为完全按照书上的指导来进行,所以失败了。这第二个版本我增加了很多我自己的东西,虽然和书上的内容已经不完全一样了,但可以按照我的想法实现指令,而且我个人感觉更加的简单易懂,通俗高效,所以如果说通过这次实验让我明白了什么的话,最大的感触就是必能完全按照书上的指导来进行实验,既然是实验,那么必须要有自己原创的部分,而且,也只有经过自己原创来实现的实验,来完成的功能,才能说明自己已经完完全全的掌握了这个实验怎么进行,怎么操作,还有以后如果想要更改的话,怎么在现有的基础上进行优化和删改。
截止到3月28日晚1点,我设计出来的模拟机已经经过了22次大改,34次功能仿真,14次下载验证,其他大大小小的改动不计其数,最终得到的版本已经是我觉得我能达到的巅峰状态了,可以很完美的实现所要求的全部七条指令,可以在01000地址进行判断的时候执行强读RAM和强写RAM的操作,可以说在我能够想到的所有的测试情况中已经没有任何的bug了,我也觉得仅凭我自己已经没有办法再做什么大型的优化了。

六、以下是我的实验日志:

时间:3月19日
地点:工训中心
内容:主要还是完成控制器,对于控制器的rom的设计还是感觉没有底气,总感觉有些rom的指令有问题,但不知道哪里有问题,将微程序控制器进行连线,看了一下模拟机的指导,感觉前途灰暗。。。

时间:3月20日
地点:305宿舍
内容:和舍友冯昂的rom的信号设计进行和相互的比较,讨论之后感觉收获很大,修改了很多,但是微程序控制器的时钟信号感觉出现了一些问题,还需要进行修改
晚上12点,已经完成了微程序控制器的修改,仿真的结果感觉也没有问题,接下来就剩下载验证了

时间:3月21日
地点:工训中心&305宿舍
内容:完成了微程序控制器的下载验证,感觉没有问题,可以单拍运行也可以持续运行,但感觉并不是太懂如何操作,还是需要再熟悉一下。模拟机正式开始,将各个元件进行了创立,并在工程中进行了连接,但感觉并没有什么头绪,肯定不能简单的进行连接,还需要进一步地阅读资料才行

时间:3月23日
地点:工管院
内容:完成了模拟机的第一个版本,但感觉。。。怎么说呢。。。很乱,信号乱七八糟,而且大部分都是一堆的叉号,不能用,绝对有问题,还是不能完全按照树上的来,书上的很多元件的很多端口之前设计的实验中都没有,不能完全按照书上来,这个应该就是废掉了,还需要一个新的版本进行设计。

时间:3月25日
地点:工训中心
内容:完成了模拟机的训练题(训练七),感觉懂了很多,之前一直不明白为什么会有四个时钟周期,也不懂为什么会有那么多的信号(28个,真的搞人),还是需要再看一下书,感觉书上的另外几个图还是有些用的,比方说那个时序的图

时间:3月27日
地点:305宿舍
内容:将第二个版本初步连接出来了,感觉还不错,图形上没有问题,但时序上的整合还是需要设计

时间:3月28日
地点:工训中心&305宿舍
内容:第二个版本的电路整合已经没有问题了,对于时序的设计感觉也没有问题了,但一直有一个死循环,而且不是一直死循环,是前两个指令运行正常,在一个010指令运行的时候就读不进去,而且ir寄存器的输入,控制信号都引出来进行查看了,也没有问题,不知道到底为什么
晚上8点30分,总算查出来了,错了一个字母。。。
没有问题了,只是还是需要对强读强写信号进行一下设计,感觉虽然可以进入强读和强写的循环,但是没有办法对pc进行一个写的操作,还是需要更正和优化。
晚上10点20分,完成了强读信号和强写信号的优化,感觉已经没有什么明显的bug了,至少我已经找不出来了,开始写实验报告好了。
晚上11点30分,完成了实验报告的初稿