摘要#
透過 quartus 軟體,使用 verilog 語言,採用了結構化行為描述方法,完成了單週期 CPU 模型機的設計和仿真,實現了模型機的正常運行
關鍵字: 整體、系統
第一章 原理與模型機設計#
1.1 實驗有關原理#
冯诺依曼計算機工作原理#
- 計算機由控制器、運算器、存儲器、輸入設備、輸出設備五大部分組成。
- 程序和數據以二進制代碼形式不加區別地存放在存儲器中,存放位置由地址確定。
- 控制器根據存放在存儲器中的指令序列(程序)進行工作,並由一個程序計數器控制指令的執行。
-
單週期 CPU:指的是一條指令的執行在一個時鐘週期內完成,然後開始下一條指令的執行,即一條指令用一個時鐘週期完成。
-
指令系統:指一台計算機的所有的指令合集。
-
指令週期:是從取指令、分析指令到執行完該指令所需的時間,不同的指令其指令週期長短可以不同。
-
單週期 CPU 處理指令的步驟:
取指令 -> 指令譯碼 -> 指令執行 -> 存儲器訪問 -> 結果寫回
1.2 模型機設計#
結構框圖#
控制線路圖#
對應模塊及功能#
InstructionMemory: 指令存儲器,依據輸入的地址從存儲器中取出相對應的指令。
CU: 控制單元,對指令進行分析,確定應該進行什麼操作,並按照確定的時序向相應的部件發出控制信號。
Register: 數據寄存器 (图 1.2.4),負責將 alu 的運算數據和存儲器中取出和存入的數據進行暫存,起到緩存的作用,因為單週期內不能同時讀寫,所以加入了一级緩存的設計。
ALU: 算術邏輯單元 (图 1.2.5),依據輸入的操作碼和數據進行相應的算數運算和邏輯運算。
memRam: 數據存儲器 (图 1.2.6),對數據進行存儲
PC: 程序計數器,進行取值操作,完成第一條指令的執行,而後根據 PC 取出第二條指令的地址。PC 中的地址自動加一或由轉移指令給出下一條指令的地址。
第二章 原理與模型機設計#
2.1 操作碼格式#
本次實驗採用的是定長編碼,機器指令 16 位編碼,故根據實際需求設計出以下操作碼:
`define ADD 4'b0000 //算術加
`define INC 4'b0001 //算術加1
`define NOT 4'b0010 //邏輯非
`define AND 4'b0011 //邏輯與
`define OR 4'b0100 //邏輯或
`define SLF 4'b0101 //數據左移
`define SRF 4'b0110 //數據右位
`define JMP 4'b0111 //無條件轉移
`define STO 4'b1000 //寫存儲器,存數
`define LAD 4'b1001 //讀存儲器,取數
`define MOV 4'b1010 //數據傳送
`define HAT 4'b1111 //暫停
2.2 指令與格式#
=> 算數運算指令#
(1) ADD rd, rs, rt
0000 | rd (四位) | rs (四位) | rt (四位) |
---|---|---|---|
功能:rd <- rs + rt (算數加)
(2) INC rt, rs
0001 | rt (四位) | rs (四位) | 0000 (未用) |
---|---|---|---|
功能:rt <- rs (算數加 1)
=> 邏輯運算指令#
(3) NOT rt, rs
0010 | rt (四位) | rs (四位) | 0000 (未用) |
---|---|---|---|
功能:rt <- !rs (邏輯非運算)
(4) AND rd, rs, rt
0011 | rd (四位) | rs (四位) | rt (四位) |
---|---|---|---|
功能:rd <- rs & rt (邏輯與運算)
(5) OR rd, rs, rt
0100 | rd (四位) | rs (四位) | rt (四位) |
---|---|---|---|
功能:rd <- rs | rt (邏輯或運算)
=> 移位指令#
(6) SLF rd, rs, rt
0101 | rd (四位) | rs (四位) | rt (四位) |
---|---|---|---|
功能:rd <- rs<<rt (左移)
(7) SRF rd, rt, rs
0110 | rd (四位) | rs (四位) | rt (四位) |
---|---|---|---|
功能:rd <- rs>>rt (右移)
=> 存儲器讀寫指令#
(8) STO rt, rs
1000 | rt (三位) | 0 (未用) | rs (四位) | 0000 (未用) |
---|---|---|---|---|
功能:將數據從寄存器寫入到數據存儲器
(9) LAD rt, rs
1001 | rt (四位) | rs (三位) | 00000 (未用) |
---|---|---|---|
功能:將數據從數據存儲器讀出到寄存器
=> 無條件轉移指令#
(10) JMP
0111 | 0000 (未用) | 轉移指令地址 (八位) |
---|---|---|
功能:跳轉到指定的指令地址
=> 停機指令#
(11) HLT
1111 | 000000000000 (未用) |
---|---|
功能:停機,PC 的值保持不變
第三章 模型機實現與測試#
3.1Verilog 程序設計#
headfile.v#
`ifndef HEADFILE_H_
`define ADD 4'b0000 //算術加
`define INC 4'b0001 //算術加1
`define NOT 4'b0010 //邏輯非
`define AND 4'b0011 //邏輯與
`define OR 4'b0100 //邏輯或
`define SLF 4'b0101 //數據左移
`define SRF 4'b0110 //數據右位
`define JMP 4'b0111 //無條件轉移
`define STO 4'b1000 //寫存儲器,存數
`define LAD 4'b1001 //讀存儲器,取數
`define MOV 4'b1010 //數據傳送
`define HAT 4'b1111 //暫停
`define rg0 4'b0000 //寄存器0
`define rg1 4'b0001 //寄存器1
`define rg2 4'b0010 //寄存器2
`endif
alu.v#
`timescale 1ns / 1ps
`include "headfile.v"
//ALU,進行邏輯運算和算數運算
module alu(op, a, b, n, f);
input [3:0] op, n;
input [7:0] a, b;
output [7:0] f;
reg [7:0] f;
always@(*)
begin
case(op)
`ADD: f = a + b;
`INC: f = a + 1;
`NOT: f = ~a;
`AND: f = a&b;
`OR: f = a|b;
`SLF: f = a<<n;
`SRF: f = a>>n;
default: f = 8'b00000000;
endcase
end
endmodule
memRam.v#
`timescale 1ns / 1ps
`include "headfile.v"
//存儲器
module memRam(data,wren,address,inclock,outclock,q);
parameter wordsize = 8;
parameter memsize = 8;
parameter addr = 3; //3位地址線
input [wordsize-1:0] data;
input [addr-1:0] address;
input wren,inclock,outclock;
output [wordsize-1:0] q;
reg [wordsize-1:0] q;
reg [wordsize-1:0] ram [memsize-1:0];
integer i;
initial
begin//初始化
for(i=0; i<8; i=i+1)
ram[i]=8'b00000000;
ram[0]=8'b00000010; //在第0位寫入2
end
always@(posedge inclock) //inclock上升沿觸發
begin
if(~wren)
ram[address] = data; //wren為低電平時,將data寫入到對應地址
end
always@(posedge outclock) //outclock上升沿觸發
begin
if(wren)
q = ram[address]; //wren為高電平時,讀出對應地址的數據
end
endmodule
Register.v#
`timescale 1ns / 1ps
`include "headfile.v"
//寄存器
module Register(clk,data,wren,inaddr,outaddr1,outaddr2,
regtoalu1,regtoalu2,regtomemaddr,regtomem,
memtoregwren,memtoregaddr,memtoregdata);
input [7:0] data;
input [3:0] inaddr,outaddr1,outaddr2,regtomemaddr,memtoregaddr;
input wren,clk,memtoregwren;
output [7:0] regtoalu1,regtoalu2,regtomem,memtoregdata;
reg [7:0] regmem [15:0];
reg lwren,lmemtoregwren;
reg [3:0] linaddr,lmemtoregaddr;
reg [7:0] ldata,lmemtoregdata;
integer i;
initial
begin//初始化
lwren = 1'b0;
lmemtoregwren = 1'b0;
for(i=0; i<16; i=i+1)
regmem[i]=8'b00000000;
end
always@(posedge clk) //緩存
begin
lwren <= wren;
linaddr <= inaddr;
ldata <= data;
lmemtoregwren <= memtoregwren;
lmemtoregaddr <= memtoregaddr;
lmemtoregdata <= memtoregdata;
end
always@(*)
begin
if(lwren)
regmem[linaddr] <= ldata; //將data寫入到對應地址
if(lmemtoregwren)
regmem[lmemtoregaddr] <= lmemtoregdata;
end
assign regtoalu1 = regmem[outaddr1]; //從寄存器取值
assign regtoalu2 = regmem[outaddr2];
assign regtomem = regmem[regtomemaddr];
endmodule
InstructionMemory.v#
`timescale 1ns / 1ps
`include "headfile.v"
//存儲指令
module InstructionMemory(A, RD);
input [7:0] A;
output [15:0] RD;
reg [15:0] IM [29:0];
assign RD = IM[A];//立即根據地址取出內容
//執行成功後,存儲器中從1到7位的數據應為:2,3,5,2,3,253,250,126
initial begin
IM[0] = {`LAD, `rg0, 3'b000, 5'b00000}; //從存儲器第0位讀取數據到寄存器rg0,rg0 = 2
/*-----------------------------------------------------------------*/
IM[1] = {`INC, `rg1, `rg0, 4'b0000}; //將寄存器rg0的數據算術加1並將結果移到rg1,rg1 = rg0 + 1 = 2 + 1 = 3
IM[2] = {`STO, 3'b001, 1'b0, `rg1, 4'b0000}; //將寄存器rg1中的數據存入存儲器第1位,3
/*-----------------------------------------------------------------*/
IM[3] = {`ADD, `rg2, `rg1, `rg0}; //將寄存器rg0和rg1中的數相加並將結果存入rg2,rg2=rg0+rg1=2+3=5
IM[4] = {`STO, 3'b010, 1'b0, `rg2, 4'b0000}; //將寄存器rg2中的數據存入存儲器第2位,5
/*-----------------------------------------------------------------*/
IM[5] = {`JMP, 4'b0000, 8'b00000111}; //跳轉到第七條指令
IM[6] = {`HAT, 12'b000000000000}; //若沒有跳轉成功,就會停機
/*-----------------------------------------------------------------*/
IM[7] = {`AND, `rg2, `rg1, `rg0}; //將寄存器rg0和rg1中的數進行與運算並存入rg2,rg2 = rg1 & rg0 = 00000011 & 00000010 = 00000010(2)
IM[8] = {`STO, 3'b011, 1'b0, `rg2, 4'b0000}; //將寄存器rg2中的數據存入存儲器第3位,2
/*-----------------------------------------------------------------*/
IM[9] = {`OR, `rg2, `rg1, `rg0}; //將寄存器rg0和rg1中的數進行或運算並存入rg2,rg2 = rg1 | rg0 = 00000011 | 00000010 = 00000011(3)
IM[10] = {`STO, 3'b100, 1'b0, `rg2, 4'b0000}; //將寄存器rg2中的數據存入存儲器第4位,3
/*-----------------------------------------------------------------*/
IM[11] = {`NOT, `rg2, `rg0, 4'b0000}; //將寄存器rg0的數據進行非運算並將結果存入rg2,rg2 = ~rg0 = ~00000010 = 11111101(253)
IM[12] = {`STO, 3'b101, 1'b0, `rg2, 4'b0000}; //將寄存器rg2中的數據存入存儲器第5位,253
/*-----------------------------------------------------------------*/
IM[13] = {`SLF, `rg0, `rg2, 4'b0001}; //rg2的數據左移一位並將結果存入rg0,rg0 = rg2<<1 = 11111101<<1 = 11111010(250)
IM[14] = {`STO, 3'b110, 1'b0, `rg0, 4'b0000}; //將寄存器rg0中的數據存入存儲器第6位,250
/*-----------------------------------------------------------------*/
IM[15] = {`SRF, `rg1, `rg2, 4'b0001}; //rg2的數據右移一位並將結果存入rg1,rg1 = rg2>>1 = 11111101>>1 = 01111110(126)
IM[16] = {`STO, 3'b111, 1'b0, `rg1, 4'b0000}; //將寄存器rg2中的數據存入存儲器第7位,126
/*-----------------------------------------------------------------*/
IM[17] = {`HAT, 12'b000000000000}; //停機
IM[18] = 16'b0000000000000000;
IM[19] = 16'b0000000000000000;
IM[20] = 16'b0000000000000000;
IM[21] = 16'b0000000000000000;
IM[22] = 16'b0000000000000000;
IM[23] = 16'b0000000000000000;
IM[24] = 16'b0000000000000000;
IM[25] = 16'b0000000000000000;
IM[26] = 16'b0000000000000000;
IM[27] = 16'b0000000000000000;
IM[28] = 16'b0000000000000000;
IM[29] = 16'b0000000000000000;
end
endmodule
CU.v#
`timescale 1ns / 1ps
`include "headfile.v"
//控制不同指令下對數據的分發
module CU(
input [15:0] instr,
output enable,
output reg [3:0] regoutaddr1,
output reg [3:0] regoutaddr2,
output reg [3:0] reginaddr,
output reg [3:0] regtomemaddr,
output reg [3:0] memtoregaddr,
output reg [3:0] aluop,
output reg [3:0] alun,
output reg memwren,
output reg memtoregwren,
output reg [2:0] memaddr,
output reg [7:0] pcnextaddr,
output reg pcnext,
output reg pcflag,
output reg regwren);
wire [3:0] op;
assign op = instr[15:12];
initial
begin
regwren = 1'b0;
memtoregwren <= 1'b0;
memwren = 1'b1;
pcnext = 1'b0;
pcflag = 1'b0;
end
always@(*)
begin
if((op == `ADD)||(op == `AND)||(op == `OR))
begin
aluop <= instr[15:12];
regoutaddr1 <= instr[3:0];
regoutaddr2 <= instr[7:4];
regwren <= 1'b1;
reginaddr <= instr[11:8];
end
else if((op == `SLF)||(op == `SRF))
begin
aluop <= instr[15:12];
alun <= instr[3:0];
regoutaddr1 <= instr[7:4];
regwren <= 1'b1;
reginaddr <= instr[11:8];
end
else if((op == `INC)||(op == `NOT))
begin
aluop <= instr[15:12];
regoutaddr1 <= instr[7:4];
regwren <= 1'b1;
reginaddr <= instr[11:8];
end
else if((op == `STO))
begin
regtomemaddr <= instr[7:4];
memaddr <= instr[11:9];
memwren <= 1'b0;
end
else if((op == `LAD))
begin
memaddr <= instr[7:5];
memwren <= 1'b1;
memtoregaddr <= instr[11:8];
memtoregwren <= 1'b1;
end
else
begin
regwren <= 1'b0;
memtoregwren <= 1'b0;
//memwren <= 1'b1;
end
end
always@(*)
begin
if((op == `JMP))
begin
pcnextaddr <= instr[7:0];
pcnext <= 1'b1;
pcflag <= 1'b1;
end
else
pcnext <= 1'b0;
end
assign enable = ~(op == `HAT);
endmodule
CPU_top.v#
`timescale 1ns / 1ps
//頂層模塊,用於連接各模塊
module CPU_top(
input clk,
input reset,
output [7:0] OPT_PC
);
reg [7:0] PC;
wire [15:0] instr;
wire [7:0] aluout;
wire [3:0] alun;
wire [3:0] aluop;
wire regwren,enable,memwren,memtoregwren,pcnext,pcflag;
wire [2:0] memaddr;
wire [3:0] memtoregaddr;
wire [3:0] Reginaddr;
wire [3:0] Regoutaddr1;
wire [3:0] Regoutaddr2;
wire [3:0] regtomemaddr;
wire [7:0] Registerout1;
wire [7:0] Registerout2;
wire [7:0] memtoregdata;
wire [7:0] regtomem;
wire [7:0] NextPC;
wire [7:0] pcnextaddr;
initial begin
PC = 8'b00000000;
end
InstructionMemory IM(
.A(PC),
.RD(instr)
);
CU m0(
.instr(instr),
.enable(enable),
.regoutaddr1(Regoutaddr1),
.regoutaddr2(Regoutaddr2),
.reginaddr(Reginaddr),
.regtomemaddr(regtomemaddr),
.memtoregaddr(memtoregaddr),
.aluop(aluop),
.alun(alun),
.memwren(memwren),
.memtoregwren(memtoregwren),
.memaddr(memaddr),
.pcnextaddr(pcnextaddr),
.pcnext(pcnext),
.pcflag(pcflag),
.regwren(regwren)
);
Register R(
.clk(clk),
.data(aluout),
.wren(regwren),
.inaddr(Reginaddr),
.outaddr1(Regoutaddr1),
.outaddr2(Regoutaddr2),
.regtoalu1(Registerout1),
.regtoalu2(Registerout2),
.regtomemaddr(regtomemaddr),
.regtomem(regtomem),
.memtoregwren(memtoregwren),
.memtoregaddr(memtoregaddr),
.memtoregdata(memtoregdata)
);
alu A(
.op(aluop),
.a(Registerout1),
.b(Registerout2),
.n(alun),
.f(aluout)
);
memRam M(
.data(regtomem),
.wren(memwren),
.address(memaddr),
.inclock(clk),
.outclock(clk),
.q(memtoregdata)
);
assign NextPC = (pcnext) ? pcnextaddr : (PC + 1'b1); //判定是跳轉到指定地址還是執行下一條指令
always@(posedge clk) //時序,每個週期PC值變化一次
begin
if(reset)
PC <= 0;
else
begin
if(enable)
PC <= NextPC;
else
PC <= PC; //停機指令,PC值保持不變
end
end
assign OPT_PC = PC;
endmodule
3.2 測試程序#
CPU_test.v#
`timescale 1ns / 1ps
module CPU_test(OPT_PC);
output [7:0] OPT_PC;
reg clk;
reg reset;
CPU_top uut(
.clk(clk),
.reset(reset),
.OPT_PC(OPT_PC)
);
//執行成功後,存儲器中從1到7位的數據應為:2,3,5,2,3,253,250,126
initial begin
clk = 0;
reset = 1; //初始化CPU
#100;
reset = 0;
$display(" pc: instr : ALUR :rg0:rg1:rg2: m0: m1: m2: m3: m4: m5: m6: m7");
$monitor("%d:%b:%b:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",
uut.PC, uut.instr, uut.aluout, uut.R.regmem[0], uut.R.regmem[1], uut.R.regmem[2],
uut.M.ram[0], uut.M.ram[1], uut.M.ram[2], uut.M.ram[3],
uut.M.ram[4], uut.M.ram[5], uut.M.ram[6], uut.M.ram[7] );
#2000 $stop;
end
always #50 clk = ~clk;
endmodule
3.3 模型機執行程序的過程的分析#
仿真得到的波形圖#
控制台輸出#
指令的分析#
-
指令 0:從存儲器第 0 位讀取數據到寄存器 rg0,rg0=2,存儲器第 0 位為 2
-
指令 1:將寄存器 rg0 的數據算術加 1 並將結果移到 rg1,rg1 = rg0+1=2+1=3
-
指令 2:將寄存器 rg1 中的數據存入存儲器第 1 位,存儲器第 1 位為 3
-
指令 3:將寄存器 rg0 和 rg1 中的數相加並將結果存入 rg2,rg2=rg0+rg1=2+3=5
-
指令 4:將寄存器 rg2 中的數據存入存儲器第 2 位,存儲器第 2 位為 5
-
指令 5:跳轉到第七條指令
-
指令 6:若沒有跳轉成功,就會停機
-
指令 7:將寄存器 rg0 和 rg1 中的數進行與運算並存入 rg2,rg2=00000011&00000010=00000010
-
指令 8:將寄存器 rg2 中的數據存入存儲器第 3 位,存儲器第 3 位為 2
-
指令 9:將寄存器 rg0 和 rg1 中的數進行或運算並存入 rg2,rg2=
00000011|00000010=00000011 -
指令 10:將寄存器 rg2 中的數據存入存儲器第 4 位,存儲器第 4 位為 3
-
指令 11:將寄存器 rg0 的數據進行非運算並將結果存入 rg2,rg2=~00000010=11111101(253)
-
指令 12:將寄存器 rg2 中的數據存入存儲器第 5 位,存儲器第 5 位為 253
-
指令 13:rg2 的數據左移一位並將結果存入 rg0,rg0=rg2<<1=11111101<<1=11111010(250)
-
指令 14:將寄存器 rg0 中的數據存入存儲器第 6 位,存儲器第 6 位為 250
-
指令 15:rg2 的數據右移一位並將結果存入 rg1,rg1=rg2>>1=11111101>>1=01111110(126)
-
指令 16:將寄存器 rg2 中的數據存入存儲器第 7 位,存儲器第 7 位為 126
-
指令 17:停機
結果分析#
- 指令 0-2:測試算數加一,計算 2+1=3,得到 3 存儲在存儲器第 1 位上
- 指令 3-4:測試加法,計算 2+3=5,得到 5 存儲在存儲器第 2 位上
- 指令 5-6,測試跳轉指令
- 指令 7-8,測試與運算,計算 00000011 & 00000010,得到 3 存儲在存儲器第 2 位上
- 指令 9-10,測試或運算,計算 00000011 | 00000010,得到 4 存儲在存儲器第 3 位上
- 指令 11-12,測試非運算,計算~00000010,得到 253 存儲在存儲器第 5 位上
- 指令 13-14,測試左移,11111101<<1,得到 250 存儲在存儲器第 6 位上
- 指令 15-16,測試右移,11111101>>1,得到 126 存儲在存儲器第 7 位上
從波形圖 (图 3.3.1) 中可以看到從指令 5 跳轉到指令 7,跳轉指令成功運行,而指令 17 後每個週期 PC 的值都不變。
從控制台的輸出 (图 3.3.2) 中可以看到存儲器中的數值為:2,3,5,2,3,253,250,126 和對指令的分析是一致的。