我是 蛋糕店老板,一名国科大数字IC在读研究生,研究兴趣是类脑神经网络芯片设计。
关注公众号,拉你进“IC设计交流群
”。
【蛋糕店老板 致力于写原创文章,希望将有深度、有帮助的干货文章带给大家~】
文章目录
我们重点关注代码的写入逻辑和输出逻辑
1 AXI-Lite源代码关键信号分析
1.1AXI-Lite-Slave 源码分析
12行 parameter integer C_S_AXI_DATA_WIDTH = 32,
14行 parameter integer C_S_AXI_ADDR_WIDTH = 4
源码中首先定义了数据位宽和地址位宽,我在生成该源码时设定DATA_WIDTH= 32,ADDR_WIDTH= 4
问题来了:ADDR_WIDTH 在这里为什么只有4?看一下代码:
231行 case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
101行 localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
102行 localparam integer OPT_MEM_ADDR_BITS = 1;
以上三行代码要一起看:
这段的意思是对寄存器寻址,学过计组就应该知道,计算机中1word = 32bit = 4Bytes, CPU按照字节寻址,
所以寻字节的过程地址+1,寻字就是地址+4;这里是寻找一个word(一个寄存器存一个word),就应该地址增4;故 ADDR_LSB=2
本代码设定该接口有四个寄存器,故OPT_MEM_ADDR_BITS = 1代表寻址范围是【3:2】也就是4个
ADDR_LSB在32bits datawith 下是2,OPT_MEM_ADDR_BITS在定义4个寄存器情况下为1,地址总线的2、3位从00变换到01、10、11其实就是地址信号axi_awaddr在不停加4。也就验证了不同寄存器的地址偏移4。即地址总线的第2、3bit位决定了数据总线访问哪一个寄存器。
231行 case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0:
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
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
238行 end
这里展示了对字节寻址的逻辑:数据发来每次以字节写入,分四次写完一个寄存器。整个always块的解释为: 在一个时钟周期内,对寄存器写操作时,地址以4递增寻址,如果寻址到slv_reg0【31:0】就分四次,每次一字节对slv_reg0进行赋值(32bits位宽)。如果寻址到slv_reg1就分四次,每次1字节对slv_reg0进行赋值,依此类推
其中(byte_index*8) +: 8解释:
如果byte_index = 0,那么以上可计算为0 *8+:8 = 0+:8 , A+:B的意思是A向上增加B位,0+:8代表[7:0] 数据段
一定要注意,这里展示的是写数据
逻辑,AXI源码写和读是分开的
369行 always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
379行 end
这里展示的是AXI4读逻辑,只不过采用的是组合逻辑,axi_araddr信号寻址然后对应地址数据(寄存器中的数据)输出。
这里需要注意的是:
四个寄存器从一个固定端口reg_data_out输出,我们以后在修改逻辑时可能需要注意这个端口。
393行 if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
396行 end
124行 assign S_AXI_RDATA = axi_rdata;
这里可以看到,读出的信号从S_AXI_RDATA 端口送出,也符合S_AXI_RDATA是读地址端口
1.2AXI-Lite-Master 源码分析
首先分析Master如何判断地址
1.2.1 Master地址逻辑——AXI究竟如何寻址?
12行 parameter C_M_START_DATA_VALUE = 32'hAA000000,
15行 parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000,
Slave的定义基地址C_M_TARGET_SLAVE_BASE_ADDR
183行 assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr;
下面这个逻辑是自定义的寻址代码,毕竟我们是用状态机来控制Master,一般我们都是采用MCU来控制
所以下面的地址生成和数据生成逻辑是User Logic
446行 always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
axi_awaddr <= 0;
end
else if (M_AXI_AWREADY && axi_awvalid)
begin
axi_awaddr <= axi_awaddr + 32'h00000004;
end
459行 end
M_AXI_AWADDR即写地址端口,在基地址的基础上自增4进行寻址
1.2.2 Master写数据逻辑
462行 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
else if (M_AXI_WREADY && axi_wvalid)
begin
axi_wdata <= C_M_START_DATA_VALUE + write_index;
end
474行 end
关键代码:axi_wdata <= C_M_START_DATA_VALUE + write_index;
274行always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
write_index <= 0;
end
else if (start_single_write)
begin
write_index <= write_index + 1;
end
286行 end
可以看到,数据生成User Logic是采用自增一的方式记录写索引
1.2.3 Master状态机分析
202行 assign init_txn_pulse = (!init_txn_ff2) && init_txn_ff;
206行 always @(posedge M_AXI_ACLK)
begin
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
219行 end
init_txn_pulse 采用脉冲信号激活状态机
257行 if (start_single_write)
begin
259行 axi_awvalid <= 1'b1;
当start_single_write有效,开始一次写传输,设置axi_awvalid有效
257行 if (start_single_write)
begin
axi_awvalid <= 1'b1;
end
else if (M_AXI_AWREADY && axi_awvalid)
begin
264行 axi_awvalid <= 1'b0;
写数据有效
else if (start_single_write)
begin
axi_wvalid <= 1'b1;
end
//Data accepted by interconnect/slave (issue of M_AXI_WREADY by slave)
else if (M_AXI_WREADY && axi_wvalid)
begin
axi_wvalid <= 1'b0;
写有效
总结:start_single_write信号会引发一次传输的写数据有效和写有效
类似的,start_single_read开启一次读传输,具体代码就不再分析
借用米联客uisrc的一张图,展示Master的核心状态机逻辑。
状态机代码有些长,就不再博文上贴出,仔细分析会发现:
从代码本身的注释说明的顺序能看出来,master执行写后读的顺序。
写后读之所以重要,为什么该lite协议能够明白何时写,何时读。就是采用状态机将master划分为四个状态。写状态运行完毕后再读,避免冲突。
以下是之前写的一页ppt