一、设计目的
系统掌握计算机的组成和工作原理,能够自助熟练准确地阐述计算机执行机器指令的工作过程,熟练应用并设计微指令、微程序的设计及调试。
二、设计内容
模型机与程序运行试验是一个综合性整机实验。该模型机包含7条机器指令,它能够依照用户执行微程序完成由加、与、非运算以及数据组合的任意复合运算。用户测试程序可以通过内存初始化的方式存储在内存中,也可以通过强迫写的方式循环写入内存。
这里我采用分模块整合法,此整机实验,由节拍脉冲、数据通路、微程序控制器、数码管显示4个模块组成。运算器、存储器、数据通路及微程序控制器中的时钟脉冲必须与时序电路相连。
1.以下为参考顶层电路:
2.设计指令表如下:
这个实验主要通过以下三个步骤完成:
首先,将各个元件进行设计,并生成相应的bsf文件,注意这里最好对每一个元件进行单独的仿真验证和测试,然后通过仿真结果进行检测,看看是否正常运行。这里每一个元件的源程序都可以在书上找到。
其次,将各个元件进行连接,先进行线路上的链接,将相同的信号进行连接,然后对整个电路进行仿真验证。这里的链接方法在书上都有,就是那个顶层实体图,然后时钟信号根据微体系结构图得到
最后,根据仿真验证的结果,对整个电路进行微调,主要是对时钟信号进行调整。
三、详细设计
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(我自己设置的,用来控制下址)元件;如图所示:
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寄存器模块,用来访问内存文件,产生指令,产生计数,存储指令和地址)元件;
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元件为:
其中的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附加电路说明
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仿真结果说明
具体分析如下:
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文件展示
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文件为:
下载图示:
1、针脚设置:
芯片还是选取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计数器的数值。具体代码如下:
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分,完成了实验报告的初稿