基于AHB的四通道DMA控制器设计-dmac_channel_ctrl.v
- 管 4 个通道、4 个货架
- 管源地址、目标地址、搬运长度
- 管什么时候读、什么时候写
- 管 FIFO 什么时候放货、什么时候取货
- 指挥 AHB 控制器去主干道运输
dmac_channel_ctrl 是 DMA 的 4 通道总调度员。老师(CPU)下达任务后,调度员根据源地址、目标地址、搬运长度,控制两个状态机分别处理 “内存→外设” 和 “外设→内存” 两种搬运方向,自动地址自增、计数、判断 FIFO 空满,指挥 AHB 控制器完成运输,并把快递暂存到对应货架,任务完成后通知系统。整个过程全自动,不用老师插手。
`timescale 1ns/10ps
// ==============================
// 模块:dmac_channel_ctrl
// 功能:DMA 4通道总控制器(总调度员)
// 场景:【助理手下的快递总调度】
// 作用:统一管理4路无人机运输,控制地址、计数、FIFO、状态机
// ==============================
module dmac_channel_ctrl(
input clk, // 工作节拍
input rst, // 系统复位,全部重来
// 4个通道的请求信号:谁要干活
input req_0, req_1, req_2, req_3,
// 对接AHB控制器(道路管理员)
output wr, // 写命令:送快递
output rd, // 读命令:取快递
output [31:0] addr, // 地址:去哪取/送到哪
output [31:0] wdata, // 要送的快递
input [31:0] rdata, // 取回来的快递
input rd_en, // 取到有效快递
// 通道使能:哪个通道启动
input en_0,en_1,en_2,en_3,
// 传输方向:0=内存→外设 1=外设→内存
input target_0,target_1,target_2,target_3,
// 每个通道要搬多少快递
input [9:0] ch_0_size,ch_1_size,ch_2_size,ch_3_size,
// 源地址、目标地址
input [31:0] ch_0_sour,ch_0_dest,
input [31:0] ch_1_sour,ch_1_dest,
input [31:0] ch_2_sour,ch_2_dest,
input [31:0] ch_3_sour,ch_3_dest,
// FIFO空/满状态
input fifo_0_empty,fifo_1_empty,fifo_2_empty,fifo_3_empty,
input fifo_0_full,fifo_1_full,fifo_2_full,fifo_3_full,
// 控制FIFO:放快递、取快递
output wr_fifo_0, wr_fifo_data_0, rd_fifo_0,
input [31:0] rd_fifo_data_0,
output wr_fifo_1, wr_fifo_data_1, rd_fifo_1,
input [31:0] rd_fifo_data_1,
output wr_fifo_2, wr_fifo_data_2, rd_fifo_2,
input [31:0] rd_fifo_data_2,
output wr_fifo_3, wr_fifo_data_3, rd_fifo_3,
input [31:0] rd_fifo_data_3,
input hready_in, // 对方已就绪
output req_done, // 本次传输完成
// 4个通道任务完成标志
output reg ch_0_t0_done,
output reg ch_1_t0_done,
output reg ch_2_t0_done,
output reg ch_3_t0_done
);
parameter MAX_TRANS=8; // 一次最多搬8个
parameter TRANS_W=3;
// 每个通道已搬运计数
reg [9:0] cnt_t0_0,cnt_t0_1,cnt_t0_2,cnt_t0_3;
reg [TRANS_W-1:0] t0_cnt; // 突发传输计数器
wire t0_mt_done; // 一次突发完成
// 方向0:内存 → 外设(取快递→送办公室)
wire peri_t0_req;
wire mem_t0_req;
// 方向1:外设 → 内存(办公室→送回快递点)
wire peri_t1_req;
wire mem_t1_req;
// 通道使能
wire ch0_t0_en,ch1_t0_en,ch2_t0_en,ch3_t0_en;
wire ch0_t1_en,ch1_t1_en,ch2_t1_en,ch3_t1_en;
// 地址寄存器
reg [31:0] rd_addr_0,rd_addr_1,rd_addr_2,rd_addr_3;
wire [31:0] t0_rm_addr; // 读内存地址
wire [31:0] t0_wp_addr; // 写外设地址
reg t0_wr,t0_rd; // 方向0读写
wire t0_done; // 方向0全部完成
wire [31:0] t1_rp_addr; // 读外设地址
wire [31:0] t1_wm_addr; // 写内存地址
reg t1_wr,t1_rd; // 方向1读写
// FIFO控制
wire wr_fifo;
wire [31:0] wr_fifo_data;
wire rd_fifo;
// 使能延时
reg en_0_d,en_1_d,en_2_d,en_3_d;
wire en_0_r,en_1_r,en_2_r,en_3_r;
reg en_0_r_d,en_1_r_d,en_2_r_d,en_3_r_d;
reg [31:0] rd_addr_t1_0,rd_addr_t1_1,rd_addr_t1_2,rd_addr_t1_3;
// ==============================
// 方向0状态机:内存 → 外设(取快递→送办公室)
// ==============================
reg [5:0] s0_cs; // 当前状态
reg [5:0] s0_ns; // 下一状态
parameter S0_IDLE = 6’b000000; // 空闲
parameter S0_R_M = 6’b000001; // 读内存(取快递)
parameter S0_WAIT_R = 6’b000010; // 等待取到
parameter S0_W_P = 6’b000100; // 写外设(送办公室)
parameter S0_WAIT_W = 6’b001000; // 等待送到
// 通道0~3方向0使能
assign ch0_t0_en=en_0 && target_0==0;
assign ch1_t0_en=en_1 && target_1==0;
assign ch2_t0_en=en_2 && target_2==0;
assign ch3_t0_en=en_3 && target_3==0;
// 外设请求:FIFO非空,可以送
assign peri_t0_req =(req_0&&ch0_t0_en&&fifo_0_empty==0)||
(req_1&&ch1_t0_en&&fifo_1_empty==0)||
(req_2&&ch2_t0_en&&fifo_2_empty==0)||
(req_3&&ch3_t0_en&&fifo_3_empty==0);
// 内存请求:需要取快递
assign mem_t0_req=(ch0_t0_en|ch1_t0_en|ch2_t0_en|ch3_t0_en)&&~t0_done;
// 状态机时序逻辑
always @(posedge clk or negedge rst)
if (!rst) s0_cs<=S0_IDLE;
else s0_cs<=s0_ns;
// 状态机组合逻辑
always @(*)
case (s0_cs)
S0_IDLE: begin
if (peri_t0_req) s0_ns=S0_W_P; // 送快递
else if (mem_t0_req) s0_ns=S0_R_M; // 取快递
else s0_ns=S0_IDLE;
end
S0_R_M: s0_ns=S0_WAIT_R; // 取货后等待
S0_WAIT_R: begin
if (rd_en && (t0_mt_done||t0_done)) s0_ns=S0_IDLE;
else if (rd_en && ~t0_mt_done) s0_ns=S0_R_M;
else s0_ns=S0_WAIT_R;
end
S0_W_P: s0_ns=S0_WAIT_W; // 送货
S0_WAIT_W: s0_ns=hready_in ? S0_IDLE : S0_WAIT_W;
default : s0_ns=S0_IDLE;
endcase
// 通道0搬运计数
always @(posedge clk or negedge rst)
if (!rst) cnt_t0_0<=0;
else if (target_0) cnt_t0_0<=0;
else if (en_0 && s0_cs==S0_R_M) cnt_t0_0<=cnt_t0_0+1;
// 通道1/2/3计数(逻辑一样)
always @(posedge clk or negedge rst)
if (!rst) cnt_t0_1<=0; else if (target_1) cnt_t0_1<=0;
else if (en_1 && s0_cs==S0_R_M) cnt_t0_1<=cnt_t0_1+1;
always @(posedge clk or negedge rst)
if (!rst) cnt_t0_2<=0; else if (target_2) cnt_t0_2<=0;
else if (en_2 && s0_cs==S0_R_M) cnt_t0_2<=cnt_t0_2+1;
always @(posedge clk or negedge rst)
if (!rst) cnt_t0_3<=0; else if (target_3) cnt_t0_3<=0;
else if (en_3 && s0_cs==S0_R_M) cnt_t0_3<=cnt_t0_3+1;
// 突发传输计数器
always @(posedge clk or negedge rst)
if (!rst) t0_cnt<=0;
else if (s0_cs==S0_IDLE) t0_cnt<=0;
else if(s0_cs==S0_R_M) t0_cnt<=t0_cnt+1;
assign t0_mt_done = t0_cnt==0 && s0_cs==S0_WAIT_R;
// 通道0~3任务完成标志
always @(posedge clk or negedge rst)
if (!rst) ch_0_t0_done<=0;
else if (en_0 && target_0==0 && cnt_t0_0==ch_0_size)
ch_0_t0_done<=1;
always @(posedge clk or negedge rst)
if (!rst) ch_1_t0_done<=0;
else if (en_1 && target_1==0 && cnt_t0_1==ch_1_size)
ch_1_t0_done<=1;
always @(posedge clk or negedge rst)
if (!rst) ch_2_t0_done<=0;
else if (en_2 && target_2==0 && cnt_t0_2==ch_2_size)
ch_2_t0_done<=1;
always @(posedge clk or negedge rst)
if (!rst) ch_3_t0_done<=0;
else if (en_3 && target_3==0 && cnt_t0_3==ch_3_size)
ch_3_t0_done<=1;
// 任一通道完成即总完成
assign t0_done = ch_0_t0_done|ch_1_t0_done|ch_2_t0_done|ch_3_t0_done;
// 方向0读写控制
always @(posedge clk or negedge rst)
if (!rst) t0_wr<=0; else t0_wr <= (s0_cs==S0_W_P);
always @(posedge clk or negedge rst)
if (!rst) t0_rd<=0; else t0_rd <= (s0_cs==S0_R_M);
// 源地址自增:每取一个快递,地址+4
always @(posedge clk or negedge rst)
if (!rst) rd_addr_0<=0;
else if (en_0 && cnt_t0_0==0) rd_addr_0<=ch_0_sour;
else if (en_0 && s0_cs==S0_R_M) rd_addr_0<=rd_addr_0+4;
always @(posedge clk or negedge rst)
if (!rst) rd_addr_1<=0;
else if (en_1 && cnt_t0_1==0) rd_addr_1<=ch_1_sour;
else if (en_1 && s0_cs==S0_R_M) rd_addr_1<=rd_addr_1+4;
// 通道2、3地址自增(逻辑同上)
// 选择当前使用的源/目标地址
assign t0_rm_addr = en_0 ? rd_addr_0 :
en_1 ? rd_addr_1 :
en_2 ? rd_addr_2 :
en_3 ? rd_addr_3 : 0;
assign t0_wp_addr = en_0 ? ch_0_dest :
en_1 ? ch_1_dest :
en_2 ? ch_2_dest :
en_3 ? ch_3_dest : 0;
// ==============================
// 方向1状态机:外设 → 内存(办公室→送回快递点)
// ==============================
reg [5:0] s1_cs;
reg [5:0] s1_ns;
parameter S1_IDLE = 6’b000000;
parameter S1_R_P = 6’b000001; // 读外设
parameter S1_WAIT_R = 6’b000010; // 等待读到
parameter S1_W_M = 6’b000100; // 写内存
parameter S1_WAIT_W = 6’b001000; // 等待写完
// 方向1使能
assign ch0_t1_en=en_0 && target_0==1;
assign ch1_t1_en=en_1 && target_1==1;
assign ch2_t1_en=en_2 && target_2==1;
assign ch3_t1_en=en_3 && target_3==1;
// 外设请求:FIFO不满,可以收
assign peri_t1_req =(req_0&&ch0_t1_en&&fifo_0_full==0)||
(req_1&&ch1_t1_en&&fifo_1_full==0)||
(req_2&&ch2_t1_en&&fifo_2_full==0)||
(req_3&&ch3_t1_en&&fifo_3_full==0);
assign mem_t1_req = ch0_t1_en|ch1_t1_en|ch2_t1_en|ch3_t1_en;
// 状态机时序
always @(posedge clk or negedge rst)
if (!rst) s1_cs<=S1_IDLE; else s1_cs<=s1_ns;
// 状态机组合逻辑
always @(*)
case (s1_cs)
S1_IDLE: begin
if (peri_t1_req) s1_ns=S1_R_P;
else if (mem_t1_req) s1_ns=S1_W_M;
else s1_ns=S1_IDLE;
end
S1_R_P: s1_ns=S1_WAIT_R;
S1_WAIT_R: s1_ns=rd_en ? S1_IDLE : S1_WAIT_R;
S1_W_M: s1_ns=S1_WAIT_W;
S1_WAIT_W: s1_ns=hready_in ? S1_IDLE : S1_WAIT_W;
default : s1_ns=S1_IDLE;
endcase
// 方向1读写控制
always @(posedge clk or negedge rst)
if (!rst) t1_wr<=0; else t1_wr <= (s1_cs==S1_W_M);
always @(posedge clk or negedge rst)
if (!rst) t1_rd<=0; else t1_rd <= (s1_cs==S1_R_P);
// 使能信号边沿检测
always @(posedge clk or negedge rst)
if (!rst) begin en_0_d<=0;en_1_d<=0;en_2_d<=0;en_3_d<=0; end
else begin en_0_d<=en_0;en_1_d<=en_1;en_2_d<=en_2;en_3_d<=en_3; end
assign en_0_r = en_0 & ~en_0_d;
assign en_1_r = en_1 & ~en_1_d;
assign en_2_r = en_2 & ~en_2_d;
assign en_3_r = en_3 & ~en_3_d;
// 方向1地址自增
always @(posedge clk or negedge rst)
if (!rst) rd_addr_t1_0<=0;
else if (en_0_r && ~en_0_r_d && target_0==1) rd_addr_t1_0<=ch_0_dest;
else if (en_0 && s1_cs==S1_WAIT_W) rd_addr_t1_0<=rd_addr_t1_0+4;
// 通道1/2/3方向1地址自增(逻辑同上)
// 方向1地址选择
assign t1_rp_addr = en_0?ch_0_sour:en_1?ch_1_sour:en_2?ch_2_sour:en_3?ch_3_sour:0;
assign t1_wm_addr = en_0?rd_addr_t1_0:en_1?rd_addr_t1_1:en_2?rd_addr_t1_2:en_3?rd_addr_t1_3:0;
// ==============================
// FIFO 控制逻辑
// ==============================
assign wr_fifo = rd_en; // 取到快递就写入FIFO
assign wr_fifo_data = rdata; // 快递内容
assign rd_fifo_t0 = s0_cs==S0_W_P; // 方向0读FIFO
assign rd_fifo_t1 = s1_cs==S1_W_M; // 方向1读FIFO
assign rd_fifo = rd_fifo_t0 | rd_fifo_t1;
// 分通道控制FIFO
assign wr_fifo_0 = en_0 && wr_fifo;
assign rd_fifo_0 = en_0 && rd_fifo;
assign wr_fifo_data_0 = en_0 ? wr_fifo_data : 0;
assign wr_fifo_1 = en_1 && wr_fifo;
assign rd_fifo_1 = en_1 && rd_fifo;
assign wr_fifo_data_1 = en_1 ? wr_fifo_data : 0;
assign wr_fifo_2 = en_2 && wr_fifo;
assign rd_fifo_2 = en_2 && rd_fifo;
assign wr_fifo_data_2 = en_2 ? wr_fifo_data : 0;
assign wr_fifo_3 = en_3 && wr_fifo;
assign rd_fifo_3 = en_3 && rd_fifo;
assign wr_fifo_data_3 = en_3 ? wr_fifo_data : 0;
// 最终输出地址选择
assign addr = t0_rd ? t0_rm_addr :
t0_wr ? t0_wp_addr :
t1_rd ? t1_rp_addr :
t1_wr ? t1_wm_addr : 0;
// 输出数据选择(从FIFO取)
assign wdata = wr ? (
en_0 ? rd_fifo_data_0 :
en_1 ? rd_fifo_data_1 :
en_2 ? rd_fifo_data_2 :
en_3 ? rd_fifo_data_3 : 0 ) : 0;
// 最终读写命令
assign wr = t0_wr | t1_wr;
assign rd = t0_rd | t1_rd;
// 传输完成信号
assign req_done = (rd_en&&t0_mt_done)||(rd_en&&t0_done)||
(s0_cs==S0_WAIT_W&&hready_in)||
(s1_cs==S1_WAIT_R&&rd_en)||
(s1_cs==S1_WAIT_W&&hready_in);
endmodule