天天看点

计算机组成原理实验报告 实验三:控制器实验(源码全)

  1. 实验类型
  2. 本实验为设计型实验。
  3. 实验目的
  4. ① 具有多周期控制器的设计能力。

    ② 掌握用 Verilog HDL 实现有限状态机的常用方法。

    ③ 熟悉 Vivado 的设计流程,具备硬件的设计仿真和测试能力。

    ④ 测试多周期控制器的功能。

  5. 实验原理
控制器是CPU 的重要组成部分,是整个计算机的控制核心。控制器的功能是按照程序预定的顺序执行每条指令。每条指令都是在控制器的控制下按照取指令、分析指令和执行指令的步骤依次完成的, 这就需要控制器必须在正确的时间准确地产生各部件的控制信号, 使计算机能够有条不紊地完成所有指令的功能。      
  1. 多周期 CPU 把指令执行分成多个阶段,每个阶段在一个时钟周期内完成,如取指、译码、执行、访存、写回。此时,不同指令所用周期数可以不同,每个周期只做一部分操作。将 CPU 划分为多周期的优势在于,每个时钟周期内 CPU 需要做的工作就变少,因此时钟周期短、时钟频率高;每个部件做的事情单一,如取指部件只负责从指令存储器中取出指令,因此 CPU 可以进行流水工作,相当于一个时钟周期完成一条指令, CPU 可以更快地运行。

    多周期 MIPS CPU 的控制部件的状态转移如下图 所示,每个状态对应一个周期。

  2. 计算机组成原理实验报告 实验三:控制器实验(源码全)
  3. 本实验根据状态及指令直接对控制信号赋值,中间变量 next_state 意为下一状态。在当前状态中,根据指令对 next_state 赋值,并在每个时钟上升沿把 next_state 存入状态寄存器,这是用 Verilog HDL 实现有限状态机时常用的方法。
  4. 实验内容和要求

    ① 学习 MIPS 指令集,深入理解常用指令的功能和编码,并进行归纳确定处理器各部件的控制码,如使用何种运算、是否写寄存器堆等。

    ② 参考附录 E,根据本实验准备实现的 20 条 MIPS 指令和最后一行跳转指令的示例,将下表补充填写完整。

    指令类型 汇编指令 指令码 源操作数1 源操作数2 目的寄存器 功能描述

    R型指令 add rd, rs, rt 000000 rs | rt | rd | 00000|100000

I型指令 addi rt, rs, imm 001000 rs|rt|imm

J型指令 j target 000010 | target PC target PC 跳转

PC={PC[31:28],target,2’b00}

③ 自行设计本实验的方案,多周期 MIPS CPU 的控制部件的大致结构如下图所示。

计算机组成原理实验报告 实验三:控制器实验(源码全)

④ 根据设计的实验方案,使用 Verilog HDL 编写相应代码。

⑤ 对编写的代码进行仿真,得到正确的波形图。

⑥ 将以上设计作为一个单独的模块,设计一个外围模块去调用该模块,如下图所示。

计算机组成原理实验报告 实验三:控制器实验(源码全)

外围模块中需调用封装好的 LCD 触摸屏模块, 观察多周期 CPU 的控制器的状态和各输出控制信号的值等,并且需要利用触摸功能输入指令的操作码、功能码,以达到实时观察控制信号变化的效果。通过这些手段,可以在实验箱上充分验证 CPU 的正确性。

源代码

mccu.v:
module mccu (op,func,z,clock,resetn,wpc,wir,wmem,wreg,iord,regrt,m2reg,aluc,shift
,alusrca,alusrcb,pcsource,jal,sext,state); 
 input [5:0] op,func; 
 input z,clock,resetn; 
 output reg wpc,wir,wmem,wreg,iord,regrt,m2reg; 
 output reg [3:0] aluc; 
 output reg [1:0] alusrcb,pcsource; 
 output reg shift,alusrca,jal,sext; 
 output reg [2:0] state; 
 reg [2:0] next_state; 
 parameter [2:0] sif = 3'b000, 
            sid = 3'b001, 
            sexe = 3'b010, 
            smem = 3'b011, 
             swb = 3'b100; 
 wire r_type,i_add,i_sub,i_and,i_or,i_xor,i_sll,i_srl,i_sra,i_jr; 
 wire i_addi,i_andi,i_ori,i_xori,i_lw,i_sw,i_beq,i_bne,i_lui,i_j,i_jal; 
 //and(r_type,~op[5],~op[4],~op[3],~op[2],~op) ;
 and(r_type,~op[5],~op[4],~op[3],~op[2],~op[1],~op[0]); 
 and(i_add,r_type,func[5],~func[4],~func[3],~func[2],~func[1],~func[0]); 
 and(i_sub,r_type,func[5],~func[4],~func[3],~func[2],func[1],~func[0]); 
 and(i_and,r_type,func[5],~func[4],~func[3],func[2],~func[1],~func[0]); 
 and(i_or,r_type,func[5],~func[4],~func[3],func[2],~func[1],func[0]); 
 and(i_xor,r_type,func[5],~func[4],~func[3],func[2],func[1],~func[0]); 
 and(i_sll,r_type,~func[5],~func[4],~func[3],~func[2],~func[1],~func[0]); 
 and(i_srl,r_type,~func[5],~func[4],~func[3],~func[2],func[1],~func[0]); 
 and(i_sra,r_type,~func[5],~func[4],~func[3],~func[2],func[1],func[0]); 
 and(i_jr,r_type,~func[5],~func[4],func[3],~func[2],~func[1],~func[0]); 
 and(i_addi,~op[5],~op[4],op[3],~op[2],~op[1],~op[0]); 
 and(i_andi,~op[5],~op[4],op[3],op[2],~op[1],~op[0]); 
 and(i_ori,~op[5],~op[4],op[3],op[2],~op[1],op[0]); 
 and(i_xori,~op[5],~op[4],op[3],op[2],op[1],~op[0]); 
 and(i_lw,op[5],~op[4],~op[3],~op[2],op[1],op[0]); 
 and(i_sw,op[5],~op[4],op[3],~op[2],op[1],op[0]); 
 and(i_beq,~op[5],~op[4],~op[3],op[2],~op[1],~op[0]); 
 and(i_bne,~op[5],~op[4],~op[3],op[2],~op[1],op[0]); 
 and(i_lui,~op[5],~op[4],~op[3],op[2],op[1],op[0]); 
 and(i_j,~op[5],~op[4],~op[3],~op[2],op[1],~op[0]); 
 and(i_jal,~op[5],~op[4],~op[3],~op[2],op[1],op[0]); 
 wire i_shift; 
 or (i_shift,i_sll,i_srl,i_sra); 
 always @ * begin 
  wpc = 0; 
  wir = 0; 
  wmem = 0; 
  wreg = 0; 
  iord = 0; 
  aluc = 4'bx000; 
  alusrca = 0; 
  alusrcb = 2'h0; 
  regrt = 0; 
  m2reg = 0; 
  shift = 0; 
  pcsource = 2'h0; 
  jal = 0; 
  sext = 1; 
  case (state) 
   sif:begin 
    wpc = 1; 
    wir = 1; 
    alusrca = 1; 
    alusrcb = 2'h1; 
    next_state = sid; 
   end 
   sid:begin 
    if(i_j)begin 
     pcsource = 2'h3; 
     wpc = 1; 
     next_state = sif; 
    end 
    else if(i_jal)begin 
     pcsource = 2'h3; 
     wpc = 1; 
     jal = 1; 
     wreg = 1; 
     next_state = sif; 
    end 
    else if(i_jr)begin 
     pcsource = 2'h2; 
     wpc = 1; 
     next_state = sif;      
mccu_display.v:
//*************************************************************************
// > 文件名: mccu_display.v
// > 描述 :mccu 显示模块,调用 FPGA 板上的 IO 接口和触摸屏
//*************************************************************************
module mccu_display(
 //时钟与复位信号
 input clk,
 input resetn, //后缀"n"代表低电平有效
//脉冲开关,用于产生脉冲 clk,实现单步执行
 input btn_clk,
 //拨码开关,用于选择输入数
 input [1:0] input_sel, //00:输入为控制信号(op)
    //10:输入为源操作数 1(func)
    //11:输入为源操作数 2(z)
 //触摸屏相关接口,不需要更改
 output lcd_rst,
 output lcd_cs,
 output lcd_rs,
 output lcd_wr,
 output lcd_rd,
 inout[15:0] lcd_data_io,
 output lcd_bl_ctr,
 inout ct_int,
 inout ct_sda,
 output ct_scl,
 output ct_rstn
 );
 //-----{时钟和复位信号}begin
//不需要更改,用于单步调试
 wire cpu_clk; //多周期 CPU 里使用脉冲开关作为时钟,以实现单步执行
reg btn_clk_r1;
reg btn_clk_r2;
 always @(posedge clk)
 begin
 if (!resetn)
 begin
 btn_clk_r1<= 1'b0;
 end
 else
 begin
 btn_clk_r1 <= ~btn_clk;
 end
 btn_clk_r2 <= btn_clk_r1;
 end
wire clk_en;
 assign clk_en = !resetn || (!btn_clk_r1 && btn_clk_r2);
 BUFGCE cpu_clk_cg(.I(clk),.CE(clk_en),.O(cpu_clk));
//-----{时钟和复位信号}end
//-----{调用 mccu 模块}begin
 reg [5:0] op; // 操作码
 reg [5:0] func; // 功能码
 //-----{此处省略,请自己编写} ALU 结果为零标志
 reg z;
 wire [3:0] aluc; // ALU 控制信号
 wire [1:0] alusrcb, pcsource;
 wire shift,alusrca,jal,sext,wpc,wir,wmem,wreg,iord,regrt,m2reg;
 wire [2:0] state;
 mccu m(
 .op(op),
 .func(func),
 .z(z),
 .clock(cpu_clk),
 .resetn(resetn),
 .wpc(wpc),
 .wir(wir),
 .wmem(wmem),
 .wreg(wreg),
 .iord(iord),
 .regrt(regrt),
 .m2reg(m2reg),
 .aluc(aluc),
 .shift(shift),
 .alusrca(alusrca),
 .alusrcb(alusrcb),
 .pcsource(pcsource),
 .jal(jal),
 .sext(sext),
 .state(state)
 //-----{此处省略,请自己编写}
);

//-----{调用 mccu 模块}end
//---------------------{调用触摸屏模块}begin--------------------//
//-----{实例化触摸屏}begin
//此小节不需要更改
 reg display_valid;
 reg [39:0] display_name;
 reg [31:0] display_value;
 wire [5 :0] display_number;
 wire input_valid;
 wire [31:0] input_value;
 lcd_module lcd_module(
 .clk (clk ), //10Mhz
 .resetn (resetn ),
 //调用触摸屏的接口
 .display_valid (display_valid ),
 .display_name (display_name ),
 .display_value (display_value ),
 .display_number (display_number),
 .input_valid (input_valid ),
 .input_value (input_value ),
 //lcd 触摸屏相关接口,不需要更改
 .lcd_rst (lcd_rst ),
 .lcd_cs (lcd_cs ),
 .lcd_rs (lcd_rs ),
 .lcd_wr (lcd_wr ),
 .lcd_rd (lcd_rd ),
 .lcd_data_io (lcd_data_io ),
 .lcd_bl_ctr (lcd_bl_ctr ),
 .ct_int (ct_int ),
 .ct_sda (ct_sda ),
 .ct_scl (ct_scl ),
 .ct_rstn (ct_rstn )
 );
//-----{实例化触摸屏}end
//-----{从触摸屏获取输入}begin
//根据实际需要输入的数修改此小节,
//建议对每一个数的输入,编写单独一个 always 块
 //当 input_sel 为 00 时,表示输入 op
 always @(posedge clk)
 begin
    if (!resetn)
    begin
        op <= 4'd0;
    end
    else if (input_valid && input_sel==2'b00)
    begin
        op <= input_value[5:0];
    end
 end
 
 always @(posedge clk)
 begin
    if (!resetn)
    begin
        func <= 4'd0;
    end
    else if (input_valid && input_sel==2'b01)
    begin
        func <= input_value[5:0];
    end
 end
 
  always @(posedge clk)
    begin
    if (!resetn)
    begin
        z <= 4'd0;
    end
    else if (input_valid && input_sel==2'b11)
    begin
        z <= input_value[0];
    end
 end
 //-----{此处省略,请自己编写}
//-----{从触摸屏获取输入}end
//-----{输出到触摸屏显示}begin
//根据需要显示的数修改此小节,
//触摸屏上共有 44 块显示区域,可显示 44 组 32 位数据
//44 块显示区域从 1 开始编号,编号为 1~44,
 always @(posedge clk)
 begin
 case(display_number)
 6'd1 :
 begin
 display_valid <= 1'b1;
 display_name <= "OP";
 display_value <= op;
 end
 6'd2 :
 begin
 display_valid <= 1'b1;
 display_name <= "FUNC";
 display_value <= func;
 end
 6'd3 :
 begin
 display_valid <= 1'b1;
 display_name <= "Z";
 display_value <= z;
 end
 6'd4 :
 begin
 display_valid <= 1'b1;
 display_name <= "STATE";
 display_value <= state;
 end
 6'd5 :
 begin
 display_valid <= 1'b1;
 display_name <= "WPC";
 display_value <= wpc;
 end
  6'd6 :
 begin
 display_valid <= 1'b1;
 display_name <= "WIR";
 display_value <= wir;
 end
  6'd7 :
 begin
 display_valid <= 1'b1;
 display_name <= "WMEM";
 display_value <= wmem;
 end
 6'd8 :
 begin
 display_valid <= 1'b1;
 display_name <= "WREG";
 display_value <= wreg;
 end
 6'd9 :
 begin
 display_valid <= 1'b1;
 display_name <= "IORG";
 display_value <= iord;
 end
 6'd10 :
 begin
 display_valid <= 1'b1;
 display_name <= "REGRT";
 display_value <= regrt;
 end
 6'd11 :
 begin
 display_valid <= 1'b1;
 display_name <= "M2REG";
 display_value <= m2reg;
 end
 6'd12 :
 begin
 display_valid <= 1'b1;
 display_name <= "ALUC";
 display_value <= aluc;
 end
 6'd13 :
 begin
 display_valid <= 1'b1;
 display_name <= "SHIFT";
 display_value <= shift;
 end
 6'd14 :
 begin
 display_valid <= 1'b1;
 display_name <= "ALUSRCA";
 display_value <= alusrca;
 end
 6'd15 :
 begin
 display_valid <= 1'b1;
 display_name <= "ALUSRVB";
 display_value <= alusrcb;
 end
 6'd16 :
 begin
 display_valid <= 1'b1;
 display_name <= "PCSOURCE";
 display_value <= pcsource;
 end
 6'd17 :
 begin
 display_valid <= 1'b1;
 display_name <= "JAL";
 display_value <= jal;
 end
 6'd18 :
 begin
 display_valid <= 1'b1;
 display_name <= "SEXT";
 display_value <= sext;
end
 //-----{此处省略,请自己编写}
      default :
    begin
 display_valid <= 1'b0;
 display_name <= 40'd0;
 display_value <= 32'd0;
    end
   endcase
 end
//-----{输出到触摸屏显示}end
//----------------------{调用触摸屏模块}end---------------------//      
mccu.xdc:
#时钟信号连接
set_property PACKAGE_PIN AC19 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]

#脉冲开关,用于输入作为复位信号,低电平有效
set_property PACKAGE_PIN Y3 [get_ports resetn]
set_property IOSTANDARD LVCMOS33 [get_ports resetn]

#脉冲开关,用于输入作为单步执行的clk
set_property PACKAGE_PIN Y5 [get_ports btn_clk]
set_property IOSTANDARD LVCMOS33 [get_ports btn_clk]

#拨码开关连接,用于输入
set_property PACKAGE_PIN AC21 [get_ports input_sel[1]]
set_property PACKAGE_PIN AD24 [get_ports input_sel[0]]
set_property IOSTANDARD LVCMOS33 [get_ports input_sel[1]]
set_property IOSTANDARD LVCMOS33 [get_ports input_sel[0]]

#触摸屏引脚连接
set_property PACKAGE_PIN J25 [get_ports lcd_rst]
set_property PACKAGE_PIN H18 [get_ports lcd_cs]
set_property PACKAGE_PIN K16 [get_ports lcd_rs]
set_property PACKAGE_PIN L8 [get_ports lcd_wr]
set_property PACKAGE_PIN K8 [get_ports lcd_rd]
set_property PACKAGE_PIN J15 [get_ports lcd_bl_ctr]
set_property PACKAGE_PIN H9 [get_ports {lcd_data_io[0]}]
set_property PACKAGE_PIN K17 [get_ports {lcd_data_io[1]}]
set_property PACKAGE_PIN J20 [get_ports {lcd_data_io[2]}]
set_property PACKAGE_PIN M17 [get_ports {lcd_data_io[3]}]
set_property PACKAGE_PIN L17 [get_ports {lcd_data_io[4]}]
set_property PACKAGE_PIN L18 [get_ports {lcd_data_io[5]}]
set_property PACKAGE_PIN L15 [get_ports {lcd_data_io[6]}]
set_property PACKAGE_PIN M15 [get_ports {lcd_data_io[7]}]
set_property PACKAGE_PIN M16 [get_ports {lcd_data_io[8]}]
set_property PACKAGE_PIN L14 [get_ports {lcd_data_io[9]}]
set_property PACKAGE_PIN M14 [get_ports {lcd_data_io[10]}]
set_property PACKAGE_PIN F22 [get_ports {lcd_data_io[11]}]
set_property PACKAGE_PIN G22 [get_ports {lcd_data_io[12]}]
set_property PACKAGE_PIN G21 [get_ports {lcd_data_io[13]}]
set_property PACKAGE_PIN H24 [get_ports {lcd_data_io[14]}]
set_property PACKAGE_PIN J16 [get_ports {lcd_data_io[15]}]
set_property PACKAGE_PIN L19 [get_ports ct_int]
set_property PACKAGE_PIN J24 [get_ports ct_sda]
set_property PACKAGE_PIN H21 [get_ports ct_scl]
set_property PACKAGE_PIN G24 [get_ports ct_rstn]

set_property IOSTANDARD LVCMOS33 [get_ports lcd_rst]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_cs]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_rs]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_wr]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_rd]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_bl_ctr]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[11]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[12]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[13]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[14]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[15]}]
set_property IOSTANDARD LVCMOS33 [get_ports ct_int]
set_property IOSTANDARD LVCMOS33 [get_ports ct_sda]
set_property IOSTANDARD LVCMOS33 [get_ports ct_scl]
set_property IOSTANDARD LVCMOS33 [get_ports ct_rstn]