1、axi_hp0_wr.v 模块代码解析
该模块实现 AXI HP 总线写入数据到 DDR3 的操作。该模块的接口如下。rst_n 为系统复位信号;i_clk、i_data_rst_n、i_data_en 和 i_data 为 FPGA 逻辑需要写入到 DDR3 的数据输入接口。i_clk 为同步时钟信号,i_data_rst_n 用于复位 FIFO,i_data_en 拉高表示数据总线 i_data有效,将被写入到 FIFO 中缓存。余下的 AXI_*信号为 AXI HP 总线接口,读取 FIFO 中的数据,送往 DDR3 的指定地址空间。
module axi_hp0_wr#(
parameter STAR_ADDR = 32'h0100_0000)
(
//系统信号
input rst_n,
//写入DDR3的数据
input i_clk,
input i_data_rst_n,
input i_data_en,
input[15:0] i_data,
//AXI总线时钟
input AXI_clk,
//AXI写地址通道
output reg[31:0] AXI_awaddr,
output[3:0] AXI_awlen,
output[2:0] AXI_awsize,
output[1:0] AXI_awburst,
output[1:0] AXI_awlock,
output[3:0] AXI_awcache,
output[2:0] AXI_awprot,
output[3:0] AXI_awqos,
output reg AXI_awvalid,
input AXI_awready,
//AXI写数据通道
output[63:0] AXI_wdata,
output[7:0] AXI_wstrb,
output reg AXI_wlast,
output reg AXI_wvalid,
input AXI_wready,
//AXI写响应通道
input[5:0] AXI_bid,
input[1:0] AXI_bresp,
input AXI_bvalid,
output AXI_bready
);
//内部信号申明
wire[7:0] wrfifo_data_count; //FIFO可读数据数量
reg wrfifo_rden; //FIFO读数据使能信号
reg[7:0] wrdata_num; //写入DDR3数据计数器
reg[3:0] cstate,nstate; //状态寄存器
parameter AXI_BURST_LEN = 16;
parameter STATE_RST = 4'h0;
parameter STATE_IDLE = 4'h1;
parameter STATE_WADD = 4'h2;
parameter STATE_WDAT = 4'h3;
parameter WRITE_DONE = 4'h4;
//缓存 FIFO,将数据从 i_clk 转到 AXI_clk 时钟域,位宽从 16bit 转到 64bit
fifo_generator_0 uut_fifo_generator_0 (
.rst(~i_data_rst_n), // input wire rst
.wr_clk(i_clk), // input wire wr_clk
.rd_clk(AXI_clk), // input wire rd_clk
.din(i_data), // input wire [15 : 0] din
.wr_en(i_data_en), // input wire wr_en
.rd_en(wrfifo_rden), // input wire rd_en
.dout(AXI_wdata), // output wire [63 : 0] dout
.full(), // output wire full
.empty(), // output wire empty
.rd_data_count(wrfifo_data_count) // output wire [7 : 0] rd_data_count
);
//将i_data_rst_n在AXI_clk时钟域打一拍
reg w_data_rst_n;
always @(posedge AXI_clk)
w_data_rst_n <= i_data_rst_n;
//AXI写状态机
always @(posedge AXI_clk or negedge rst_n) begin
if(~rst_n)begin
cstate <= STATE_RST;
end
else begin
cstate <= nstate;
end
end
always @( * ) begin
case(cstate)
STATE_RST: begin
if(w_data_rst_n) nstate = STATE_IDLE;
else nstate = STATE_RST;
end
STATE_IDLE: begin
if(!w_data_rst_n) nstate = STATE_RST;
else if(wrfifo_data_count >= 8'd16) nstate = STATE_WADD;
else nstate = STATE_IDLE;
end
STATE_WADD: begin
if(AXI_awvalid && AXI_awready) nstate = STATE_WDAT;
else nstate = STATE_WADD;
end
STATE_WDAT: begin
if(wrdata_num >= (AXI_BURST_LEN+1)) nstate = WRITE_DONE;
else nstate = STATE_WDAT;
end
WRITE_DONE: begin
nstate = STATE_IDLE;
end
default: begin
nstate = STATE_RST;
end
endcase
end
//1 个 burst 写入数据的个数计数
always @(posedge AXI_clk) begin
if (!rst_n) wrdata_num <= 'b0;
else if(cstate == STATE_WDAT) begin
if(wrdata_num == 8'd0) wrdata_num <= wrdata_num + 1'b1;
else if((wrdata_num <= AXI_BURST_LEN) && AXI_wready && AXI_wvalid)
wrdata_num <= wrdata_num + 1'b1;
else wrdata_num <= wrdata_num;
end
else wrdata_num <= 'b0;
end
//FIFO 读取使能信号产生
always @(*) begin
if (cstate == STATE_WDAT) begin
if(wrdata_num == 8'd0) wrfifo_rden <= 1'b1;
else if((wrdata_num >= 8'd1) && (wrdata_num < AXI_BURST_LEN) && AXI_wready
&& AXI_wvalid) wrfifo_rden <= 1'b1;
else wrfifo_rden <= 1'b0;
end
else wrfifo_rden <= 1'b0;
end
//AXI总线写数据时序产生
//写地址产生
always @(posedge AXI_clk)begin
if(cstate == STATE_RST) AXI_awaddr <= STAR_ADDR;
else if(AXI_awvalid && AXI_awready) AXI_awaddr <= AXI_awaddr + AXI_BURST_LEN * 8;
end
//写地址有效信号产生
always @(posedge AXI_clk) begin
if (!rst_n) AXI_awvalid <= 1'b0;
else if(cstate == STATE_WADD) begin
if(AXI_awvalid && AXI_awready) AXI_awvalid <= 1'b0;
else AXI_awvalid <= 1'b1;
end
else AXI_awvalid <= 1'b0;
end
//写数据有效信号产生
always @(posedge AXI_clk) begin
if (!rst_n) AXI_wvalid <= 1'b0;
else if((wrdata_num >= 8'd1) && (wrdata_num < AXI_BURST_LEN)) AXI_wvalid <= 1'b1;
else if((wrdata_num == AXI_BURST_LEN) && !AXI_wready) AXI_wvalid <= 1'b1;
else AXI_wvalid <= 1'b0;
end
//写最后一个数据有效信号产生
always @(posedge AXI_clk) begin
if (!rst_n) AXI_wlast <= 1'b0;
else if((wrdata_num == (AXI_BURST_LEN - 1)) && AXI_wready && AXI_wvalid) AXI_wlast <= 1'b1;
else if((wrdata_num == AXI_BURST_LEN) && !AXI_wready) AXI_wlast <= 1'b1;
else AXI_wlast <= 1'b0;
end
//-----------------------------------------------------------------------------
--
//AXI HP 总线固定赋值设置
assign AXI_awsize = 3'b011; //8 Bytes per burst
assign AXI_awburst = 2'b01;
assign AXI_awlock = 2'b00;
assign AXI_awcache = 4'b0010;
assign AXI_awprot = 3'h0;
assign AXI_awqos = 4'h0;
assign AXI_wstrb = 8'hff;
assign AXI_bready = 1'b1;
assign AXI_awlen = AXI_BURST_LEN - 1;
axi_hp0_wr.v
module axi_hp0_wr#(
parameter STAR_ADDR = 32'h0100_0000)
(
//系统信号
input rst_n,
//写入DDR3的数据
input i_clk,
input i_data_rst_n,
input i_data_en,
input[15:0] i_data,
//AXI总线时钟
input AXI_clk,
//AXI写地址通道
output reg[31:0] AXI_awaddr,
output[3:0] AXI_awlen,
output[2:0] AXI_awsize,
output[1:0] AXI_awburst,
output[1:0] AXI_awlock,
output[3:0] AXI_awcache,
output[2:0] AXI_awprot,
output[3:0] AXI_awqos,
output reg AXI_awvalid,
input AXI_awready,
//AXI写数据通道
output[63:0] AXI_wdata,
output[7:0] AXI_wstrb,
output reg AXI_wlast,
output reg AXI_wvalid,
input AXI_wready,
//AXI写响应通道
input[5:0] AXI_bid,
input[1:0] AXI_bresp,
input AXI_bvalid,
output AXI_bready
);
//------------------------------------------------------------------------------------
//内部信号申明
wire[7:0] wrfifo_data_count; //FIFO可读数据数量
reg wrfifo_rden; //FIFO读数据使能信号
reg[7:0] wrdata_num; //写入DDR3数据计数器
reg[3:0] cstate,nstate; //状态寄存器
parameter AXI_BURST_LEN = 16;
parameter STATE_RST = 4'h0;
parameter STATE_IDLE = 4'h1;
parameter STATE_WADD = 4'h2;
parameter STATE_WDAT = 4'h3;
parameter WRITE_DONE = 4'h4;
//-------------------------------------------------------------------------------
//缓存FIFO,将数据从i_clk转到AXI_clk时钟域,位宽从16bit转到64bit
fifo_generator_0 uut_fifo_generator_0 (
.rst(~i_data_rst_n), // input wire rst
.wr_clk(i_clk), // input wire wr_clk
.rd_clk(AXI_clk), // input wire rd_clk
.din(i_data), // input wire [15 : 0] din
.wr_en(i_data_en), // input wire wr_en
.rd_en(wrfifo_rden), // input wire rd_en
.dout(AXI_wdata), // output wire [63 : 0] dout
.full(), // output wire full
.empty(), // output wire empty
.rd_data_count(wrfifo_data_count) // output wire [7 : 0] rd_data_count
);
//------------------------------------------------------------------------------------
//将i_data_rst_n在AXI_clk时钟域打一拍
reg w_data_rst_n;
always @(posedge AXI_clk)
w_data_rst_n <= i_data_rst_n;
//-------------------------------------------------------------------------------
//AXI写状态机
always @(posedge AXI_clk or negedge rst_n) begin
if(~rst_n)begin
cstate <= STATE_RST;
end
else begin
cstate <= nstate;
end
end
always @( * ) begin
case(cstate)
STATE_RST: begin
if(w_data_rst_n) nstate = STATE_IDLE;
else nstate = STATE_RST;
end
STATE_IDLE: begin
if(!w_data_rst_n) nstate = STATE_RST;
else if(wrfifo_data_count >= 8'd16) nstate = STATE_WADD;
else nstate = STATE_IDLE;
end
STATE_WADD: begin
if(AXI_awvalid && AXI_awready) nstate = STATE_WDAT;
else nstate = STATE_WADD;
end
STATE_WDAT: begin
if(wrdata_num >= (AXI_BURST_LEN+1)) nstate = WRITE_DONE;
else nstate = STATE_WDAT;
end
WRITE_DONE: begin
nstate = STATE_IDLE;
end
default: begin
nstate = STATE_RST;
end
endcase
end
//1个burst写入数据的个数计数
always @(posedge AXI_clk) begin
if (!rst_n) wrdata_num <= 'b0;
else if(cstate == STATE_WDAT) begin
if(wrdata_num == 8'd0) wrdata_num <= wrdata_num + 1'b1;
else if((wrdata_num <= AXI_BURST_LEN) && AXI_wready && AXI_wvalid) wrdata_num <= wrdata_num + 1'b1;
else wrdata_num <= wrdata_num;
end
else wrdata_num <= 'b0;
end
//-------------------------------------------------------------------------------
//FIFO读取使能信号产生
always @(*) begin
if (cstate == STATE_WDAT) begin
if(wrdata_num == 8'd0) wrfifo_rden <= 1'b1;
else if((wrdata_num >= 8'd1) && (wrdata_num < AXI_BURST_LEN) && AXI_wready && AXI_wvalid) wrfifo_rden <= 1'b1;
else wrfifo_rden <= 1'b0;
end
else wrfifo_rden <= 1'b0;
end
//-------------------------------------------------------------------------------
//AXI总线写数据时序产生
//写地址产生
always @(posedge AXI_clk)begin
if(cstate == STATE_RST) AXI_awaddr <= STAR_ADDR;
else if(AXI_awvalid && AXI_awready) AXI_awaddr <= AXI_awaddr + AXI_BURST_LEN * 8;
end
//写地址有效信号产生
always @(posedge AXI_clk) begin
if (!rst_n) AXI_awvalid <= 1'b0;
else if(cstate == STATE_WADD) begin
if(AXI_awvalid && AXI_awready) AXI_awvalid <= 1'b0;
else AXI_awvalid <= 1'b1;
end
else AXI_awvalid <= 1'b0;
end
//写数据有效信号产生
always @(posedge AXI_clk) begin
if (!rst_n) AXI_wvalid <= 1'b0;
else if((wrdata_num >= 8'd1) && (wrdata_num < AXI_BURST_LEN)) AXI_wvalid <= 1'b1;
else if((wrdata_num == AXI_BURST_LEN) && !AXI_wready) AXI_wvalid <= 1'b1;
else AXI_wvalid <= 1'b0;
end
//写最后一个数据有效信号产生
always @(posedge AXI_clk) begin
if (!rst_n) AXI_wlast <= 1'b0;
else if((wrdata_num == (AXI_BURST_LEN - 1)) && AXI_wready && AXI_wvalid) AXI_wlast <= 1'b1;
else if((wrdata_num == AXI_BURST_LEN) && !AXI_wready) AXI_wlast <= 1'b1;
else AXI_wlast <= 1'b0;
end
//-------------------------------------------------------------------------------
//AXI HP总线固定赋值设置
assign AXI_awsize = 3'b011; //8 Bytes per burst
assign AXI_awburst = 2'b01;
assign AXI_awlock = 2'b00;
assign AXI_awcache = 4'b0010;
assign AXI_awprot = 3'h0;
assign AXI_awqos = 4'h0;
assign AXI_wstrb = 8'hff;
assign AXI_bready = 1'b1;
assign AXI_awlen = AXI_BURST_LEN - 1;
endmodule
2、axi_hp0_rd.v 模块代码解析
该模块实现 AXI HP 总线从 DDR3 读数据的操作。该模块的接口如下。rst_n 为系统复位信号;i_clk、i_data_rst_n、i_data_rden 和 o_data 为 FPGA 逻辑从该模块读取 DDR3 的控制和数据接口。i_clk 为同步时钟信号,i_data_rst_n 用于复位 FIFO,i_data_rden 拉高表示读取 FIFO 中的数据,随后 i_data 上将输出 FIFO 中缓存的数据。余下的 AXI_*信号为 AXI HP 总线接口,写入数据到 FIFO 中供读取。module axi_hp0_rd(
//系统信号
input rst_n,
//写入DDR3的数据
input i_clk,
input i_data_rst_n,
input i_data_rden,
output[15:0] o_data,
//AXI总线时钟
input AXI_clk,
//AXI读地址通道
output reg[31:0] AXI_araddr,
output[1:0] AXI_arburst,
output[3:0] AXI_arcache,
output[3:0] AXI_arlen,
output[1:0] AXI_arlock,
output[2:0] AXI_arprot,
output[3:0] AXI_arqos,
input AXI_arready,
output[2:0] AXI_arsize,
output reg AXI_arvalid,
//AXI读数据通道
input[63:0] AXI_rdata,
input[5:0] AXI_rid,
input AXI_rlast,
output AXI_rready,
input[1:0] AXI_rresp,
input AXI_rvalid
);
//参数、信号申明
parameter STAR_ADDR = 32'h0100_0000;
parameter AXI_BURST_LEN = 16;
parameter STATE_RST = 4'h0;
parameter STATE_IDLE = 4'h1;
parameter STATE_RADD = 4'h2;
parameter READ_DONE = 4'h3;
reg[3:0] cstate,nstate;
wire[7:0] fifo_wrdata_count;
//将 i_data_rst_n 在 AXI_clk 时钟域打 2 拍
wire w_data_rst_n,pos_data_rst_n,neg_data_rst_n;
pulse_detection uut_pulse_detection_odrn(
.clk(AXI_clk),
.in_sig(i_data_rst_n), //输入信号
.out_sig(w_data_rst_n), //将输入信号同步到本地时钟域后的输出
.pos_sig(pos_data_rst_n),//输入信号的上升沿检测,高脉冲保持一个时钟周期
.neg_sig(neg_data_rst_n)//输入信号的下降沿检测,高脉冲保持一个时钟周期
);
//AXI读状态机
always @(posedge AXI_clk or negedge rst_n)begin
if(~rst_n)begin
cstate <= STATE_RST;
end
else begin
cstate <= nstate;
end
end
always @( * )begin
case(cstate)
STATE_RST: begin
if(w_data_rst_n) nstate = STATE_IDLE;
else nstate = STATE_RST;
end
STATE_IDLE: begin
if(!w_data_rst_n) nstate = STATE_RST;
else if(fifo_wrdata_count < 8'd128) nstate = STATE_RADD;
else nstate = STATE_IDLE;
end
STATE_RADD: begin
if(AXI_arready) nstate = READ_DONE;
else nstate = STATE_RADD;
end
READ_DONE: begin
if(AXI_rlast) nstate = STATE_IDLE;
else nstate = READ_DONE;
end
default: begin
nstate = STATE_RST;
end
endcase
end
//AXI HP读时序产生
//读地址有效信号产生
always @(posedge AXI_clk)begin
if(nstate == STATE_RADD) AXI_arvalid <= 1'b1;
else AXI_arvalid <= 1'b0;
end
//读地址产生
always @(posedge AXI_clk)begin
if(cstate == STATE_RST) AXI_araddr <= STAR_ADDR;
else if(AXI_arvalid && AXI_arready) AXI_araddr <= AXI_araddr + AXI_BURST_LEN * 8;
end
//DDR3读出数据缓存到FIFO,LCD显示模块读取FIFO数据,位宽从64bit转16bit
fifo_generator_2 uut_fifo_generator_2 (
.rst(~i_data_rst_n), // input wire rst
.wr_clk(AXI_clk), // input wire wr_clk
.rd_clk(i_clk), // input wire rd_clk
.din(AXI_rdata), // input wire [63 : 0] din
.wr_en(AXI_rvalid), // input wire wr_en
.rd_en(i_data_rden), // input wire rd_en
.dout(o_data), // output wire [15 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.wr_data_count(fifo_wrdata_count) // output wire [7 : 0] wr_data_count
);
//AXI HP总线固定配置
assign AXI_arsize = 3'b011; //8 Bytes per burst
assign AXI_arburst = 2'b01;
assign AXI_arlock = 2'b00;
assign AXI_arcache = 4'b0010;
assign AXI_arprot = 3'h0;
assign AXI_arqos = 4'h0;
assign AXI_rready = 1'b1;
assign AXI_arlen = AXI_BURST_LEN - 1;
axi_hp0_rd.v
module axi_hp0_rd(
//系统信号
input rst_n,
//写入DDR3的数据
input i_clk,
input i_data_rst_n,
input i_data_rden,
output[15:0] o_data,
//AXI总线时钟
input AXI_clk,
//AXI读地址通道
output reg[31:0] AXI_araddr,
output[1:0] AXI_arburst,
output[3:0] AXI_arcache,
output[3:0] AXI_arlen,
output[1:0] AXI_arlock,
output[2:0] AXI_arprot,
output[3:0] AXI_arqos,
input AXI_arready,
output[2:0] AXI_arsize,
output reg AXI_arvalid,
//AXI读数据通道
input[63:0] AXI_rdata,
input[5:0] AXI_rid,
input AXI_rlast,
output AXI_rready,
input[1:0] AXI_rresp,
input AXI_rvalid
);
//------------------------------------------------------------------------------------
//参数、信号申明
parameter STAR_ADDR = 32'h0100_0000;
parameter AXI_BURST_LEN = 16;
parameter STATE_RST = 4'h0;
parameter STATE_IDLE = 4'h1;
parameter STATE_RADD = 4'h2;
parameter READ_DONE = 4'h3;
reg[3:0] cstate,nstate;
wire[7:0] fifo_wrdata_count;
//------------------------------------------------------------------------------------
//将i_data_rst_n在AXI_clk时钟域打2拍
wire w_data_rst_n,pos_data_rst_n,neg_data_rst_n;
pulse_detection uut_pulse_detection_odrn(
.clk(AXI_clk),
.in_sig(i_data_rst_n), //输入信号
.out_sig(w_data_rst_n), //将输入信号同步到本地时钟域后的输出
.pos_sig(pos_data_rst_n), //输入信号的上升沿检测,高脉冲保持一个时钟周期
.neg_sig(neg_data_rst_n) //输入信号的下降沿检测,高脉冲保持一个时钟周期
);
//-------------------------------------------------------------------------------
//AXI读状态机
always @(posedge AXI_clk or negedge rst_n)begin
if(~rst_n)begin
cstate <= STATE_RST;
end
else begin
cstate <= nstate;
end
end
always @( * )begin
case(cstate)
STATE_RST: begin
if(w_data_rst_n) nstate = STATE_IDLE;
else nstate = STATE_RST;
end
STATE_IDLE: begin
if(!w_data_rst_n) nstate = STATE_RST;
else if(fifo_wrdata_count < 8'd128) nstate = STATE_RADD;
else nstate = STATE_IDLE;
end
STATE_RADD: begin
if(AXI_arready) nstate = READ_DONE;
else nstate = STATE_RADD;
end
READ_DONE: begin
if(AXI_rlast) nstate = STATE_IDLE;
else nstate = READ_DONE;
end
default: begin
nstate = STATE_RST;
end
endcase
end
//------------------------------------------------------------------------------------
//AXI HP读时序产生
//读地址有效信号产生
always @(posedge AXI_clk)begin
if(nstate == STATE_RADD) AXI_arvalid <= 1'b1;
else AXI_arvalid <= 1'b0;
end
//读地址产生
always @(posedge AXI_clk)begin
if(cstate == STATE_RST) AXI_araddr <= STAR_ADDR;
else if(AXI_arvalid && AXI_arready) AXI_araddr <= AXI_araddr + AXI_BURST_LEN * 8;
end
//------------------------------------------------------------------------------------
//DDR3读出数据缓存到FIFO,LCD显示模块读取FIFO数据,位宽从64bit转16bit
fifo_generator_2 uut_fifo_generator_2 (
.rst(~i_data_rst_n), // input wire rst
.wr_clk(AXI_clk), // input wire wr_clk
.rd_clk(i_clk), // input wire rd_clk
.din(AXI_rdata), // input wire [63 : 0] din
.wr_en(AXI_rvalid), // input wire wr_en
.rd_en(i_data_rden), // input wire rd_en
.dout(o_data), // output wire [15 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.wr_data_count(fifo_wrdata_count) // output wire [7 : 0] wr_data_count
);
//------------------------------------------------------------------------------------
//AXI HP总线固定配置
assign AXI_arsize = 3'b011; //8 Bytes per burst
assign AXI_arburst = 2'b01;
assign AXI_arlock = 2'b00;
assign AXI_arcache = 4'b0010;
assign AXI_arprot = 3'h0;
assign AXI_arqos = 4'h0;
assign AXI_rready = 1'b1;
assign AXI_arlen = AXI_BURST_LEN - 1;
endmodule
pulse_detection.v
//对输入信号打2拍,做边沿检测
module pulse_detection(
input clk,
input in_sig, //输入信号
output out_sig, //将输入信号同步到本地时钟域后的输出
output pos_sig, //输入信号的上升沿检测,高脉冲保持一个时钟周期
output neg_sig //输入信号的下降沿检测,高脉冲保持一个时钟周期
);
reg[1:0] in_sig_r;
always @(posedge clk)
in_sig_r <= {in_sig_r[0],in_sig};
assign pos_sig = ~in_sig_r[1] & in_sig_r[0];
assign neg_sig = in_sig_r[1] & ~in_sig_r[0];
assign out_sig = in_sig_r[1];
endmodule