axi_wready使用了自反控制,当其他条件满足时,就跳转状态。axi_wready为拉低状态时,表示处于等待状态,检测AW和W通道的V握手,以及latch使能状态,满足条件时,产生R握手,并跳转到R握手状态。
if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
begin
axi_wready <= 1'b1;
end
else
begin
axi_wready <= 1'b0;
end
axi_awready使用了自反控制,当其他条件满足时,就跳转状态。axi_awready为拉低状态时,表示处于等待状态,检测AW和W通道的V握手,以及latch使能状态,满足条件时,产生R握手,并跳转到R握手状态。
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
axi_awready <= 1'b1;
end
else
begin
axi_awready <= 1'b0;
end
其中的关键控制信号,是aw_en。
来看看aw_en,
aw_en也用了自反控制,但是为了防止死锁,复位时,aw_en的初态是拉高有效。
当接口拉高axi_awready的同时,aw_en被拉低。表示不再能够latch。直到B通道的VR握手成功,才再次拉高,表示能够latch。
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
aw_en <= 1'b1;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
aw_en <= 1'b0;
end
else if (S_AXI_BREADY && axi_bvalid)
begin
aw_en <= 1'b1;
end
end
end
可以看出,aw_en的latch状态,依赖于B通道的VR握手。
来看看axi_bvalid,
axi_bvalid也使用了自反控制,当其他条件满足时,就跳转状态。当axi_bvalid为拉低状态时,表示处于等待状态,可以产生下一个V握手。当axi_bvalid为拉高状态时,表示处于V握手状态,正在检测对端的R握手。一旦检测到R握手,那么就跳转到等待状态。
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
end
else
begin
if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
begin
axi_bvalid <= 1'b1;
end
else
begin
if (S_AXI_BREADY && axi_bvalid)
begin
axi_bvalid <= 1'b0;
end
end
end
end
来看看axi_awaddr,它的latch条件和axi_awready的拉高条件是一样的,所以接口在锁存了AWADDR的同时,拉高了AWREADY,通知对端可以修改。
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
axi_awaddr <= S_AXI_AWADDR;
end
end
end
来看看slv_reg_wren,它的信号状态,依赖于AW和W通道的VR握手状态。
对于S_AXI接口而言,当axi_awaddr成功锁存的同时,就会一起拉高axi_wready和 axi_awready。在下一个周期,被锁存的axi_awaddr就可以被使用了。
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
来看看slv_reg0。
它受控于slv_reg_wren的状态,且根据前一周期锁存的axi_awaddr的地址,进行分支判断,当满足地址条件时,将WDATA的数据,锁存到slv_reg0中。
这里可以看出,锁存axi_awaddr会比锁存slv_reg0提前一个周期。
slv_reg0锁存然后输出的节拍点,是AW通道和W通道,检测到VR握手的节拍点。
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
end
else begin
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
6'h00:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
default : begin
slv_reg0 <= slv_reg0;
end
endcase
end
end
end
再来看axi_arready。
axi_arready,
axi_arready使用了自反控制,当其他条件满足时,就跳转状态。axi_arready为拉低状态时,表示处于等待状态,检测AR通道的V握手,满足条件时,产生R握手,并跳转到R握手状态。
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID)
begin
axi_arready <= 1'b1;
end
else
begin
axi_arready <= 1'b0;
end
end
end
来看看axi_araddr,
当axi_arready满足条件,从等待状态跳转到R握手状态的同时,axi_araddr锁存ARADDR。下一个周期,就可以使用这个锁存好的axi_araddr了。
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_araddr <= 32'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID)
begin
axi_araddr <= S_AXI_ARADDR;
end
end
end
来看看reg_data_out。
它利用锁存好的axi_araddr进行分支判断,当地址条件满足时,选择对应的reg_in。
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
6'h00 : reg_data_out <= slv_reg0;
6'h01 : reg_data_out <= reg1_in;
6'h02 : reg_data_out <= reg2_in;
6'h03 : reg_data_out <= reg3_in;
default : reg_data_out <= 0;
endcase
end
来看看axi_rvalid。
axi_rvalid使用了自反控制,当其他条件满足时,就跳转状态。当axi_rvalid为拉低状态时,表示处于等待状态,可以产生下一个V握手。当axi_rvalid为拉高状态时,表示处于V握手状态,正在检测对端的R握手。一旦检测到R握手,那么就跳转到等待状态。
可以看出,锁存了axi_araddr的下一个周期,才会生成axi_rvalid的拉高有效。
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
end
else
begin
if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
begin
axi_rvalid <= 1'b1;
end
else if (axi_rvalid && S_AXI_RREADY)
begin
axi_rvalid <= 1'b0;
end
end
end
来看看slv_reg_rden。它的信号状态,依赖于AR通道的VR握手状态,以及RVALID的等待状态。
这里可以看出,这个信号的条件,其实就是axi_rvalid拉高有效的条件。
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
来看看axi_rdata。
它受控于slv_reg_rden信号。
这里可以看出,axi_rdata的锁存条件,和axi_rvalid的生成条件是一样的。所以,axi_data和axi_rvalid是同时打出数据的。
另外,锁存了axi_araddr的下一个周期,才会生成axi_rvalid的拉高有效。而axi_data和axi_rvalid是同时打出数据的,所以,axi_data的打出,比锁存axi_araddr,晚一个周期。
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rdata <= 0;
end
else
begin
if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
end
end
end
来看看reg0_out。
从S_AXI接口接收的数据,如果需要向外引出,那么如下代码即可。
来看看reg0_out。
从S_AXI接口接收的数据,如果需要向外引出,那么如下代码即可。
assign reg0_out = slv_reg0;
来看看reg1_in。
从外部输入的数据,如果想从S_AXI接口发送出去,那么如下代码即可。
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
6'h00 : reg_data_out <= slv_reg0;
6'h01 : reg_data_out <= reg1_in;
6'h02 : reg_data_out <= reg2_in;
6'h03 : reg_data_out <= reg3_in;
default : reg_data_out <= 0;
endcase
end