基于AHB的四通道DMA控制器设计-dmac_ahb_ctrl.v
老师=CPU,DMA=全能助理,无人机=运输工具,AHB=主干道,AHB控制器=道路管理员,FIFO=办公室中转货架
// 本模块:DMA的AHB主控制器 —— 【道路管理员】
// 作用:按照AHB主干道规则,帮DMA助理收发快递(数据)
`timescale 1ns / 10ps
module dmac_ahb_ctrl(
// 系统时钟与复位 —— 道路节拍、系统重启
input clk, // 时钟:道路每一拍走一步
input rst, // 复位:清空任务,重新开始
// 来自DMA助理的命令 —— 助理让道路管理员干活
input wr, // 写请求:送快递
input rd, // 读请求:取快递
input [31:0]addr, // 地址:快递点/目的地地址
input [31:0]wdata, // 写数据:要送出去的快递
// 送回给DMA助理的结果 —— 告诉助理取到快递了
output [31:0]rdata, // 读到的数据:取回来的快递
output rd_en, // 读有效:快递已到手,可以拿走
// AHB 主干道外部信号 —— 道路规则信号
output hsel, // 选中从设备:找到目标快递点
output [1:0]htrans, // 传输类型:开始一次新运输
output [2:0]hsize, // 数据位宽:一次运多大包裹(32位)
output hwrite, // 读写方向:1=送快递,0=取快递
output [31:0]haddr, // AHB总线上的地址:报位置
output reg [31:0]hwdata, // AHB写数据:放到主干道上的快递
input hreadyin, // 从设备就绪:对方准备好了,可以交接
input hresp, // 响应:运输是否出错
input [31:0]hrdata // 读回数据:从主干道拿到的快递
);
// 道路管理员的3种工作状态
parameter IDLE = 3’b001; // 空闲:没事干,待命
parameter S0 = 3’b010; // 地址阶段:报地址、报任务
parameter S1 = 3’b100; // 数据阶段:真正搬快递
// 内部寄存器:用来暂存地址、数据、延时打拍(保证流水线对齐)
reg [31:0] addr_d; // 暂存目标地址
reg [31:0] data_1d; // 暂存要送的快递
reg wr_d ; // 写请求延时1拍
reg wr_2d; // 写请求延时2拍
reg rd_d ; // 读请求延时1拍
reg rd_2d; // 读请求延时2拍
reg [2:0] c_state; // 当前状态
reg [2:0] n_state; // 下一个状态
// ====================== 状态机第一段:时序更新 ======================
// 每来一个时钟节拍,更新当前状态
always @(posedge clk or negedge rst) begin
if(!rst)
c_state <= IDLE; // 复位→空闲
else
c_state <= n_state; // 否则走到下一步
end
// ====================== 状态机第二段:组合逻辑判断下一步 ======================
// 道路管理员根据当前状态,决定下一步做什么
always @(*) begin
case (c_state)
IDLE : begin // 空闲
if(wr||rd) // 助理派任务:送/取快递
n_state = S0; // → 进入地址周期
else
n_state = IDLE; // 没任务,继续休息
end
S0 : begin // 地址周期:报完地址立刻进数据周期
n_state = S1;
end
S1 :begin // 数据周期:等对方就绪
if(hreadyin==1’b1 && (wr||rd)) // 对方就绪+还有任务
n_state = S0; // 连续运输
else if(hreadyin==1’b1) // 对方就绪+没任务
n_state = IDLE; // 结束,回家
else
n_state = S1; // 继续等待
end
default: n_state = IDLE ;
endcase
end
// ====================== 请求信号延时打拍(AHB流水线必须) ======================
// 让请求信号延时1~2拍,和地址、数据对齐,不跑飞
always@(posedge clk or negedge rst) begin
if (!rst) begin
wr_d <= 1’b0;
wr_2d <= 1’b0;
rd_d <= 1’b0;
rd_2d<= 1’b0;
end else begin
wr_d <= wr;
rd_d <= rd;
wr_2d <=wr_d;
rd_2d<= rd_d;
end
end
// ====================== 锁存地址:记住要去哪个快递点 ======================
always@(posedge clk or negedge rst) begin
if(!rst) begin
addr_d<=32’h0;
end else if(wr || rd) begin // 有任务时,把地址记下来
ddr_d<=addr;
end
end
// ====================== 锁存要送的快递 ======================
always@(posedge clk or negedge rst) begin
if(!rst) begin
data_1d<=32’h0;
end else if(wr) begin // 送快递时,先把快递存好
data_1d<=wdata;
end
end
// ====================== 产生AHB控制信号 ======================
// 选中设备:找到快递点
assign hsel = (c_state==S0)||(c_state==S1 &&(wr_d || rd_d)) ;
// 传输类型:NONSEQ(开始一次新运输)
assign htrans = ( hsel == 1’b1 )? 2’h2:2’h0;
// 数据位宽:32位(标准快递大小)
assign hsize = ( hsel == 1’b1 )? 3’h2:3’h0;
// 读写方向:送快递=1,取快递=0
assign hwrite =wr_d ;
// 输出地址到主干道
assign haddr = ( hsel == 1’b1 )?addr_d:32’h0;
// ====================== 往主干道放要送的快递 ======================
always@(posedge clk or negedge rst) begin
if(!rst) begin
hwdata<=32’h0;
end else if(hsel) begin // 选中目标后,把快递放路上
hwdata<=data_1d;
end
end
// ====================== 读有效信号:快递取到了! ======================
// 条件:数据周期 + 对方就绪 + 是取快递任务
assign rd_en = c_state == S1 && hreadyin==1’b1&& (rd_2d);
// 把取到的快递交给DMA助理
assign rdata = (rd_en == 1’b1 ) ? hrdata : 32’h0;
endmodule