天天看點

計算機組成原理實驗報告 實驗三:控制器實驗(源碼全)

  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]