ZYNQ AXI协议学习(AXI_Lite,PL端为主机)
上一篇文章分析了AXI_Lite总线PS端为主机,PL端为从机的通讯方式
创建一个AXI_lite接口的IP核(PL为主机)vivado版本用的2019
对于上面一些端口的理解
status,状态组里面,INIT_AXI_TXN是一个开始触发信号,ERROR和TXN_DONE是用来判断读写错误和读写完成的信号
还是和上一篇文章一样,先在线仿真,在进行代码得分析
写操作(PL->PS)
使用官方的代码进行在线DEbug,分析代码
注:图上是没有INIT_AXI_TXN信号的,
assign init_txn_pulse = (!init_txn_ff2) && init_txn_ff;//检测上升沿
//Generate a pulse to initiate AXI transaction.
always @(posedge M_AXI_ACLK)
begin
// Initiates AXI transaction delay
if (M_AXI_ARESETN == 0 )
begin
init_txn_ff <= 1'b0;
init_txn_ff2 <= 1'b0;
end
else
begin
init_txn_ff <= INIT_AXI_TXN;
init_txn_ff2 <= init_txn_ff;
end
end
这里就是开始信号的逻辑,来一个上升沿就会触发
写操作分析
由在线的仿真图可以看出,写数据和写地址是同时发生的,AWREADY和WREADY一直为高,说明PS端一直处于写准备好状态。
对于AWPROT端口:
assign M_AXI_AWPROT = 3'b000; 代码中直接赋值为000,这里就不用理会。
WTRSB端口:
assign M_AXI_WSTRB = 4'b1111;一样没什么意义。
除去这些固定端口,剩下的好分析了。
这里只分析关键代码,剩余的可以参考仿真图与源码自己查看
写地址:
assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr;//初始地址加上偏移地址
代码的初始地址是0x40000000,这里我修改为了0x10000000,因为我板子只有512M的内存
//Write Addresses
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
axi_awaddr <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (M_AXI_AWREADY && axi_awvalid)
begin
axi_awaddr <= axi_awaddr + 32'h00000004;
end
end
开始信号到来复位偏移地址,写地址有效(在仿真图里可以看到,只有一个周期的高电平)加上一个4的偏移地址等待下一次写
写数据:
parameter C_M_START_DATA_VALUE = 32'hAA000000,
//测试数据,赋予初始值
// Write data generation
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
begin
axi_wdata <= C_M_START_DATA_VALUE;
end
// Signals a new write address/ write data is
// available by user logic
else if (M_AXI_WREADY && axi_wvalid)
begin
axi_wdata <= C_M_START_DATA_VALUE + write_index;
end
end
逻辑和写地址一样,写数据有效就加1(write_index),仿真图也能看出数据每次加1。
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
write_index <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (start_single_write)
begin
write_index <= write_index + 1;
end
end
该逻辑就是write_index的加法逻辑,start_single_write该信号有效就加1
start_single_write该信号的高低跳变在状态机写逻辑里面,该信号也是写操作一次跳变一次(一个周期)
写回应:
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
axi_bready <= 1'b0;
end
// accept/acknowledge bresp with axi_bready by the master
// when M_AXI_BVALID is asserted by slave
else if (M_AXI_BVALID && ~axi_bready)
begin
axi_bready <= 1'b1;
end
// deassert after one clock cycle
else if (axi_bready)
begin
axi_bready <= 1'b0;
end
// retain the previous value
else
axi_bready <= axi_bready;
end
//Flag write errors
assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]);
M_AXI_BRESP信号的第1位用于判断是否写入错误
读操作(PL->PS)
使用官方的代码进行在线DEbug,分析代码
读操作分析
在读地址通道,ARREADY信号一直处于高电平,PS端一直处于读地址准备好的状态。
ARPROT端口:
assign M_AXI_ARPROT = 3'b001;//这里直接赋值
读地址:
assign M_AXI_ARADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_araddr;
//和写地址一样也是初始地址加上读偏移地址
//Read Addresses
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
axi_araddr <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (M_AXI_ARREADY && axi_arvalid)
begin
axi_araddr <= axi_araddr + 32'h00000004;
end
end
这个逻辑是每读一次,偏移地址加4(默认的偏移地址是0)
读数据:
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
axi_rready <= 1'b0;
end
// accept/acknowledge rdata/rresp with axi_rready by the master
// when M_AXI_RVALID is asserted by slave
else if (M_AXI_RVALID && ~axi_rready)
begin
axi_rready <= 1'b1;
end
// deassert after one clock cycle
else if (axi_rready)
begin
axi_rready <= 1'b0;
end
// retain the previous value
end
//Flag write errors
assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]); //判断是否读取成功
当M_AXI_RVALID拉高后,PL端拉高rready信号,读取数据
状态机分析
该代码存在4个状态,空闲(IDLE),写状态(INIT_WRITE),读状态(INIT_READ),比较状态(INIT_CONPARE)
默认初始化处于空闲状态,开始信号触发后,跳转至写状态,写满4个数据(C_M_TRANSACTIONS_NUM = 4),跳转至读状态,读取刚才写入的4个数据。跳转至比较状态进行数据对比,对比完成后跳转至空闲信号等待下一次的数据操作
结语
上面分析的是一些主要的代码,官方的这个IP源码只能用于自己学习使用。状态机在应用中是不需要的,也没有人的项目是先写数据再去读数据吧。
AXI_Lite接口看仿真图和以前的分析可得,不适合数据量大的情况,没有突发传输。当数据量太多之后还是得使用哪个AXI接口(HP)。
感觉这个接口用处不大,就没有写自己的工程代码