记录剩下的7个模块,其中最后一个控制器没写完放到明天
2.指令寄存器
这里的指令寄存器设计的还是比较好理解的,从端口上来说可以有
输入:CLK、8位的DATA 、LOAD_IR、RESET
输出:16位opc_iraddrs
基本原理:在取指令LOAD_IR为高电位,CLK时钟上升沿来临时,将一条16位的指令拆分为高8位与低8位,分两次CLK上升沿拆递给8位的DATA。这里声明了一个中间变量state用于判断是高8位的指令还是低8位的指令。当中间变量state为0,进行取高8位;当中间变量state为1,进行取高低位。还需注意的是指令是存在内部的opc_iraddrs中,这里是16位的。而高3位为操作码,低13位为地址,经计算2的13次方为8192,因此寻址空间为8K字节。
有一点不是很明白:8K的指令是按照顺序存入吗?到底如何存?
Verilog代码
‘timescle 1ns/1ns
module register(opc_iraddr,clk,ena,rst,data);
input clk,rst,ena;
input [7:0] data;
output [15:0] opc_iraddr;
reg [15:0] opc_iraddr;
reg state;
always @(posedge clk)
begin
if(rst)
begin
opc_iraddr<=16’b0000_0000_0000_0000;
state<=1’b0;
end
else
begin
if(ena)
begin
casex(state)
1’b0:
begin
opc_iraddr[15:8]<=data;
state<=1’b1;
end
1’b1:
begin
opc_iraddr[7:0]<=data;
state<=1’b0;
end
default:
begin
opc_iraddr[15:0]<=16’bxxxx_xxxx_xxxx_xxxx;
state<=1’bx;
end
endcase
end
else
state<=1’b0;
end
end
endmodule
3.累加器
用于存放当前的结果,它也是双目计算中的一个来源。复位后,累加器的值为0。当累加器ena端口收到CPU状态控制器发出的load_acc信号时,在一个时钟上升沿到来时,累加器便会收到来自数据总线的数据。
Verilog代码
‘timescle 1ns/1ns
module accum(accum,data,ena,clk,rst);
input [7:0]data;
input clk,ena,rst;
output [7:0]accum;
reg [7:0]accum;
always @(posedge clk)
begin
if(rst)
accum<=8’b0000_0000;
else
if(ena)
accum<=data;
end
endmodule
4.算术运算器
根据输入的操作码,实现对应加、与、异或、跳转、写入、读出、暂停等
Verilog代码
‘timescle 1ns/1ns
module alu(alu_out,zero,data,accum,alu_ena,opcode,alu_clk);
input [7:0] data,accum;
input [2:0] opcode;
input alu_clk;
output zero;
output [7:0]alu_out;
parameter HLT = 3’b000;
SKZ = 3’b001;
ADD = 3’b010;
AND = 3’b011;
XOR = 3’b100;
LDA = 3’b101;
STO = 3’b110;
JMP = 3’b111;
assgin zero =! accum;
always @(posedge alu_clk)
begin
casex(opcode) //来自指令寄存器的输出opc_irddr[15:0]的低三位。
HLT:alu_out<=accum;
SKZ: alu_out<=accum;
ADD: alu_out<=data + accum;
AND: alu_out<= data&accum;
XOR: alu_out<= data^accum;
LDA:alu_out<= data;
STO: alu_out<=accum;
JMP: alu_out<=accum;
default: alu_out<=8’bxxxx_xxxx;
endcase
end
endmodule
5.数据控制器
控制数据data何时输出到总线上,供其他模块使用
Verilog代码
‘timescle 1ns/1ns
module datactl(data, in,data_ena);
input [7:0]in;
input data_ena;
output [7:0] data;
assign data=(data_ena)?in:8’bzzzz_zzzz;//数据不使用时,置位高阻态。
endmoudle
6.地址多路选择器
用于选择输出的地址是PC(程序计数器)地址还是数据/端口地址。每个指令周期的前4个时钟周期用于从ROM中读取指令,输出的应是PC地址;后四个时钟周期用于对RAM或端口的读写,该地址由指令给出。地址的选择输出信号由8分频信号fetch提供。
Verilog代码
‘timescle 1ns/1ns
module adr(addr,fetch,ir_addr,pc_addr);
input fetch;
input [12:0]ir_addr,pc_addr;
output [12:0]addr;
assign addr = fetch?pc_addr:ir_addr;
endmodule
7.程序计数器
用于提供指令地址
Verilog代码
‘timescle 1ns/1ns
module counter(pc_addr,ir_addr,load,rst,clock);
input [12:0]ir_addr;
input load,clock,rst;
output [12:0]pc_addr;
reg [12:0]pc_addr;
always @(posedge clock or posedge rst)
begin
if(rst)
pc_addr<=13’b0_0000_0000_0000;//复位后pc_addr从0出发
else
if(load)//这个应该是JMP指令出发的
pc_addr<=ir_addr;
else
pc_addr<=pc_addr+1;
end
endmoulde
8.状态控制器
此部分由两部分组成,状态机和状态控制器。状态机是CPU最为核心的地方
状态控制器用于控制状态机是否工作。
状态控制器Verilog代码
‘timescle 1ns/1ns
module machinectl(ena,fetch,rst,clk);
input fetch,rst,clk;
output ena;
reg ena;
reg state;
always @(posedge clk);
begin
if(rst)
begin
ena<=0
end
else
if(fetch)
begin
ena<=1;
end
end
endmodule