说明
本文中部分内容直接从夏宇闻老师的《Verilong数字系统设计教程》“简化的RISC_CPU设计”一章中摘取出来,在此基础上添加了自己的东西,仅记录自己的学习过程,欢迎一起交流学习。
1、RISC_CPU拆分
- 时钟发生器
- 指令寄存器
- 累加器
- 算术逻辑运算单元
- 数据控制器
- 状态控制器
- 程序计数器
- 地址多路器
2、时钟发生器的实现
(1)时钟发生器利用外部时钟信号生成一系列时钟信号clk1,fetch,alu_ena并送往CPU的其他部件。
fetch:控制信号,clk的8分频信号
当fetch为高电平时,使clk能触发CPU控制器开始执行一条指令;同时fetch信号还将控制地址多路器输出指令地址和数据地址。clk信号用作指令指令寄存器、累加器、状态控制器的时钟信号。alu_ena则用作算术逻辑运算单元的操作。
(2)模块框图和时序图如下所示:
(3)代码编写:
module clk_gen
(
input sys_clk,
input sys_rst_n,
output clk,
output reg fetch,
output reg alu_clk
);
reg [7:0] state;
parameter S1 = 8'b0000_0001,
S2 = 8'b0000_0010,
S3 = 8'b0000_0100,
S4 = 8'b0000_1000,
S5 = 8'b0001_0000,
S6 = 8'b0010_0000,
S7 = 8'b0100_0000,
S8 = 8'b1000_0000,
idle = 8'b0000_0000;
assign clk = sys_clk;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
begin
fetch <= 1'b0;
alu_clk <= 1'b0;
state <= idle;
end
else
begin
case(state)
S1:
begin
alu_clk <= 1'b1;
state <= S2;
end
S2:
begin
alu_clk <= 1'b0;
state <= S3;
end
S3:
begin
fetch <= 1'b1;
state <= S4;
end
S4:
begin
state <= S5;
end
S5:
begin
state <= S6;
end
S6:
begin
state <= S7;
end
S7:
begin
fetch <= 1'b0;
state <= S8;
end
S8:
begin
state <= S1;
end
idle:
begin
state <= S1;
end
default:state <= idle;
endcase
end
endmodule
(4)modelsim仿真
仿真模块代码
`timescale 1ns/1ns
module tb_clk_gen();
reg sys_clk;
reg sys_rst_n;
wire clk;
wire fetch;
wire alu_clk;
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
clk_gen clk_gen_inst
(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.clk(clk),
.fetch(fetch),
.alu_clk(alu_clk)
);
endmodule
仿真结果:
从仿真结果可以看出,该模块功能已经实现。
3、指令寄存器
(1) 指令寄存器用于寄存指令。
指令寄存器的触发时钟是clk,在clk的正沿触发下,寄存器将数据总线送来的指令存入高8位或低8位寄存器中,但不是每个clk的上升沿都寄存数据总线的数据,因为数据总线上有时传输指令,有时传输数据。什么时候寄存,什么时候不寄存由CPU状态控制器的load_ir信号控制。load_ir通过信号ena口输入到指令寄存器,复位后,指令寄存器被清为零。
每条指令为两个字节,即16位,高3位为操作码,低13位为地址(CPU的地址总线为13位,寻址空间为8K字节)。本设计的数据总线为8位,所以每条指令需要取两次。先取高8位,后取低8位。而当前是高8位还是低8位由state记录。state为0表示取的是高8位,存入高8位寄存器,同时将state置为1。
(2)模块框图和波形图!
(3)代码编写
module instruction_reg
(
input