PCLK -Peripheral clock
PADDR[..] -Peripheral address bus
PWRITE -Peripheral read/write signal
PWDATA[..] -Peripheral write data bus
PRDATA[..] -Peripheral read data bus
PSELx -Individual slave select signals
PENABLE -Peripheral data enable
由于APB只要操作register,所以APB操作只有两拍足够。 APB3和APB4两拍是不够的
PCLK -Peripheral clock
PADDR[..] -Peripheral address bus
PWRITE -Peripheral read/write signal
PWDATA[..] -Peripheral write data bus
PRDATA[..] -Peripheral read data bus
PSELx -Individual slave select signals
PENABLE -Peripheral data enable
PREADY -Peripheral cannot respnd immediately
PSLVERR -Peripheral error
APB3比APB2多了PREADY和PSLVERR信号,这两个都是output信号。 由于PREADY的信号的存在,APB3多了一个wait state的状态,所以APB3可能在两拍里面就完成不了数据传输,以有wait state的写时序为例
APB的外设可以选择产生或者不产生error response,如果不产生的话,只需要把连接APB外设的模块(比如APB bridge)对应PSLVERR端口接0即可
在以下情况下,APB的模块需要产生error response:
PCLK -Peripheral clock
PADDR[..] -Peripheral address bus
PWRITE -Peripheral read/write signal
PWDATA[..] -Peripheral write data bus
PRDATA[..] -Peripheral read data bus
PSELx -Individual slave select signals
PENABLE -Peripheral data enable
PREADY -Peripheral cannot respnd immediately
PSLVERR -Peripheral error
PPROT[..] -Some attributes signals
PSTRB[..] -Indicate which byte lanes to update in memory
Bit | 0 | 1 |
---|---|---|
PPROT[0] | Normal Access | Privileged Access |
PPROT[1] | Secure Access | Non-Secure Access |
PPROT[2] | Data | Instruction |
下面是一个APB4 Slave的Demo,简化起见,不支持PPROT信号,PREADY常为1,不支持wait state,PSLVERR常为0,不返回错误。
从上图可以看到整个IP包含两个module,一个是apb4 interface,另一个是IP的register。下图是IP内部register的定义,这个IP很简单,没有control register和status register,只有data register。
module cmsdk_apb4_eg_slave # (
paramter ADDRWIDTH = 12 )
(
input wire PCLK,
input wire PRESETn,
input wire PSEL,
input wire[ADDRWIDTH-1:0] PADDR,
input wire PENABLE,
input wire PWRITE,
input wire[31:0] PWDATA,
input wire[3:0] PSTRB,
input wire[3:0] ECOREVNUM
output wire[31:0] PRDATA,
output wire PREADY,
output wire PSLVERR);
wire[ADDRWIDTH-1:0] reg_addr;
wire reg_read_en;
wire reg_write_en;
wire[3:0] reg_byte_strobe;
wire[31:0] reg_wdata;
wire[31:0] reg_rdata;
cmsdk_apb4_eg_slave_interface
#(.ADDRWIDTH (ADDRWIDTH))
u_apb_eg_slave_interface (
// APB4 interface
.pclk (PCLK),
.presetn (PRESETn),
.psel (PSEL),
.paddr (PADDR),
.penable (PENABLE),
.pwrite (PWRITE),
.pstrb (PSTRB),
.prdata (PRDATA),
.pready (PREADY),
.pslverr (PSLVERR),
// Register interface
.addr (reg_addr),
.read_en (reg_read_en),
.write_en (reg_write_en),
.byte_strobe (reg_byte_strobe),
.wdata (reg_wdata),
.rdata (reg_data)
);
cmsdk_apb4_eg_slave_reg
#(.ADDRWIDTH(ADDRWIDTH))
u_apb_eg_slave_reg (
.pclk (PCLK),
.presetn (PRESETn),
.addr (reg_addr),
.read_en (reg_read_en),
.write_en (reg_write_en),
.byte_strobe (reg_byte_strobe),
.wdata (reg_wdata),
.ecorevnum (ECOREVNUM),
.rdata (reg_rdata)
);
endmodule
module cmsdk_apb4_eg_slave_interface #(
parameter ADDRWIDTH = 12)
(
input wire pclk,
input wire presetn,
//apb interface inputs
input wire psel,
input wire[ADDRWIDTH-1:0] paddr,
input wire penable,
input wire pwrite,
input wire[31:0] pwdata,
input wire[3:0] pstrb,
//apb interface outputs
output wire[31:0] prdata,
output wire pready,
output wire pslverr,
//register interface
output wire[ADDRWIDTH-1:0] addr,
output wire read_en,
output wire write_en,
output wire[3:0] byte_strobe,
output wire[31:0] wdata,
input wire[31:0] rdata
);
//APB interface
assign pready = 1'b1; //Always readay. Can be customized to support waitstate if required.
assign pslverr = 1'b0; //Always OKAY. Can be customized to support error response if required.
// register read and write signal
assign addr = paddr;
assign read_en = psel & (~pwrite); //assert for whole apb read transfer;
assign write_en = psel & (~penable) & pwrite; //assert for the 1st cycle of write transfer.
assign byte_strobe = pstrb;
assign wdata = pwdata;
assign prdata = rdata;
endmodule
module cmsdk_apb4_eg_slave_reg #(
parameter ADDRWIDTH = 12)
(
input wire pclk,
input wire presetn,
input wire[ADDRWIDTH-1:0] addr,
input wire read_en,
input wire write_en,
input wire[3:0] byte_strobe,
input wire[31:0] wdata,
input wire[3:0] ecorevnum,
output reg[31:0] rdata);
localparam ARM_CMSDK_APB4_EG_SLAVE_PID4 = 32'h00000004;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID5 = 32'h00000000;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID6 = 32'h00000000;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID7 = 32'h00000000;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID0 = 32'h00000019;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID1 = 32'h000000B8;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID2 = 32'h0000001B;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID3 = 32'h00000000;
localparam ARM_CMSDK_APB4_EG_SLAVE_CID0 = 32'h00000000;
localparam ARM_CMSDK_APB4_EG_SLAVE_CID1 = 32'h000000F0;
localparam ARM_CMSDK_APB4_EG_SLAVE_CID2 = 32'h00000005;
localparam ARM_CMSDK_APB4_EG_SLAVE_CID3 = 32'h000000B1;
reg[31:0] data0;
reg[31:0] data1;
reg[31:0] data2;
reg[31:0] data3;
wire[3:0] wr_sel;
//module logic start
//address decoding for write operations
assign wr_sel[0] = ((addr[(ADDRWIDTH-1):2]==10'b00000000)&(write_en)) ? 1'b1 : 1'b0;
assign wr_sel[1] = ((addr[(ADDRWIDTH-1):2]==10'b00000001)&(write_en)) ? 1'b1 : 1'b0;
assign wr_sel[2] = ((addr[(ADDRWIDTH-1):2]==10'b00000010)&(write_en)) ? 1'b1 : 1'b0;
assign wr_sel[3] = ((addr[(ADDRWIDTH-1):2]==10'b00000011)&(write_en)) ? 1'b1 : 1'b0;
//register write, byte enable
always @ (posedge pclk or negedge presetn) begin
if (~presetn) begin
data1 <= {32{1'b0}};
end else if (wr_sel[0]) begin
if (byte_strobe[0])
data0[7:0] <= wdata[7:0]
if (byte_strobe[1])
data0[15:8] <= wdata[15:8]
if (byte_strobe[2])
data0[23:16] <= wdata[23:16]
if (byte_strobe[3])
data0[31:24] <= wdata[31:24]
end
end
always @ (posedge pclk or negedge presetn) begin
if (~presetn) begin
data1 <= {32{1'b0}};
end else if (wr_sel[1]) begin
if (byte_strobe[0])
data1[7:0] <= wdata[7:0]
if (byte_strobe[1])
data1[15:8] <= wdata[15:8]
if (byte_strobe[2])
data1[23:16] <= wdata[23:16]
if (byte_strobe[3])
data1[31:24] <= wdata[31:24]
end
end
always @ (posedge pclk or negedge presetn) begin
if (~presetn) begin
data1 <= {32{1'b0}};
end else if (wr_sel[2]) begin
if (byte_strobe[0])
data2[7:0] <= wdata[7:0]
if (byte_strobe[1])
data2[15:8] <= wdata[15:8]
if (byte_strobe[2])
data2[23:16] <= wdata[23:16]
if (byte_strobe[3])
data2[31:24] <= wdata[31:24]
end
end
always @ (posedge pclk or negedge presetn) begin
if (~presetn) begin
data1 <= {32{1'b0}};
end else if (wr_sel[3]) begin
if (byte_strobe[0])
data3[7:0] <= wdata[7:0]
if (byte_strobe[1])
data3[15:8] <= wdata[15:8]
if (byte_strobe[2])
data3[23:16] <= wdata[23:16]
if (byte_strobe[3])
data3[31:24] <= wdata[31:24]
end
end
always @ (read_en or addr or data0 or data1 or data2 or data3 or ecorevnum) begin
case (read_en)
1'b1: begin
if (addr[11:4] == 8'h00) begin
case(addr[3:2])
2'b00: rdata = data0;
2'b01: rdata = data1;
2'b10: rdata = data2;
2'b11: rdata = data3;
default: rdata = {32{1'bx}};
endcase
end else if (addr[11:6] == 6'h3F) begin
case(addr[5:2])
4'b0100: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID4;
4'b0101: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID5;
4'b0110: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID6;
4'b0111: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID7;
4'b1000: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID0;
4'b1001: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID1;
4'b1010: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID2;
4'b1011: rdata = ARM_CMSDK_APB4_EG_SLAVE_PID3;
4'b1100: rdata = ARM_CMSDK_APB4_EG_SLAVE_CID0;
4'b1101: rdata = ARM_CMSDK_APB4_EG_SLAVE_CID1;
4'b1110: rdata = ARM_CMSDK_APB4_EG_SLAVE_CID2;
4'b1111: rdata = ARM_CMSDK_APB4_EG_SLAVE_CID3;
4'b0000, 4'b0001, 4'b0010, 4'b0011: rdata = {32'h00000000};
default: rdata = {32{1'bx}};
endcase
end else begin
rdata = {32'h00000000};
end
end
1'b0: begin
rdata = {32{1'b0}};
end
default: begin
rdata = {32{1'bx}};
end
endcase
end
endmodule
测试脚本很简单,就是往data1寄存器写入0x12345678,然后读出进行比较
I
L 50
W 0x10000004 0x12345678 w incr p0000 nolock okay
I
L 10
P 0x10000004 0x12345678 t3
q
上图是ahb_interface上的波形,第一次红的地方就是写数据到IP中,第二次蓝的地方就是从IP register中读数据出来,波形跟AMBA手册上的波形一模一样。
AHB-Lite Master提供地址和控制信息来标识读写操作。图1-2显示了一个AHB-Lite Master接口。
AHB-Lite Master接口
input HCLK //总线时钟
input HRESETn //总线复位信号,低有效
//From master to slave
output HADDR[31:0] //32位地址总线,输出给AHB Slave
ouput HBURST[2:0] //Burst类型,支持固定长度的4,8和16拍
ouput HMASTERLOCK //当为高时,表示当前传输是锁定序列的一部分。与地址和控制信号具有相同时序。
output HPROT[3:0] //保护控制信号提个额外的关于总线访问的信息,主要用在一些需要调用某种级别保护的Slave模块中。
ouput HSIZE[2:0] //传输大小,最大可达1024位
output HTRANS[1:0] //指示当前传输类型,可以是:
//• IDLE
//• BUSY
//• NONSEQUENTIAL
//• SEQUENTIAL.
output HWDATA[31:0] //在写操作时,写数据总线从Master传输数据到Slave。推荐最小的数据总线宽度为32。但是可以扩展为更高的操作带宽。
output HWRITE //表示传输方向。高表示写传输,低表示读传输。该信号与地址信号具有相同的时序,但在Burst传输中,必须保持常值。
//From slave to master
input HRDATA[31:0] //在读操作时,读数据总线将数据从选定的Slave传送到转换器,转换器再传送到Master。
//数据总线最小的宽度建议为32位,但也可以通过扩展使用更高的位宽。
input HREADYOUT //当为高时,HREADYOUT信号表示传输在总线上结束。该信号可以通过拉低来对传输进行扩展。
input HRESP //传输响应,如果有传输通过转换器,将向Master提供传输状态信息。
//当为低时,HRESP信号表示传输状态正常。当为高时,表示传输状态错误。
AHB-Lite Slave接口
Slave的接口正好与Master相反,只是Slave多了一个HSELx信号,该信号是从AHB仲裁器用来选择哪个Slave的信号
input HSELx //每个AHB-Lite Slave拥有自己的选择信号HSELx a,该信号表示当前传输的传输对象。
//当Slave被选中,它就必须监视HREADY信号,以确保在前一个总线传输完成后,马上开始当前传输
input HREADY //当为高时,提示Master和所有的Slave,先前的传输完成。
Master在Address phase上驱动HADDR,Slave等待若干个Clock之后送出data,在等待期间,Slave将HREADY信号拉低,SLAVE在完成传输后,将HREADY拉高,此时Master采样得到Data(A)。若无等待周期,则Slave在Address phase后面一个clock给出数据,并直接将HREADY 拉高。
写时序与读书须差不多,Master在Address phase送出地址,在Data phase将HWRITE拉高,等待Slave的HREADY拉高,否则Master移知将HWDATA维持在Data(A)。直到接受到Slave的HREADY拉高,表明一次写传输完成。
HTRANS | 类型 | 描述 |
---|---|---|
0 | IDLE | 表示没有数据传输请求。推荐Master利用IDLE传输终止锁定传输。Slaves必须始终提供一个零等待状态OKAY的响应 |
1 | BUSY | BUSY传输类型可以使Master插入IDLE周期到Burst传输的中间。该传输类型表示Master正在进行Burst传输,无法立刻执行下一个传输。当Master使用BUSY传输类型时,地址和控制信号必须对应Burst中的下一个传输。仅未定义长度的Burst传输可以具有BUSY传输作为Burst传输的最后周期。Slaves 必须始终为BUSY传输提供零等待状态OKAY响应 |
2 | NONSEQ | 表示一个单独传输或Burst传输的第一个传输地址和控制信号与前面的传输无关.单一传输可以被看做是长度为1的突发传输,因此传输类型是NONSEQUENTIAL(非连续的) |
3 | SEQ | 突发传输中剩余的传输其类型即为SEQUENTIAL,并且地址与先前的传输有关控制信息与前一个传输相同地址等于先前传输的地址+传输大小,单位byte,传输大小由信号HSIZE[2:0]定义。如果是回卷Burst,则地址回卷。 |
HSIZE[2] | HSIZE[1] | HSIZE[0] | Size(bit) | Description |
---|---|---|---|---|
0 | 0 | 0 | 8 | Byte |
0 | 0 | 1 | 16 | Halfword |
0 | 1 | 0 | 32 | Word |
0 | 1 | 1 | 64 | DoubleWord |
1 | 0 | 0 | 128 | 4-word line |
1 | 0 | 1 | 256 | 8-word line |
1 | 1 | 0 | 512 | |
1 | 1 | 1 | 1024 |
HSIZE必须小于或等于数据总线位宽。例如,一个32位数据总线,HSIZE只能是b000, b001或b010。
HBURST[2:0] | Type | Description |
---|---|---|
b000 | SINGLE | 单个Burst |
b001 | INCR | 未定义长度递增Burst |
b010 | WRAP4 | 4拍回卷Burst |
b011 | INCR4 | 4拍递增Burst |
b100 | WRAP8 | 8拍回卷Burst |
b101 | INCR8 | 8拍递增Burst |
b110 | WRAP16 | 16拍回卷Burst |
b111 | INCR16 | 16拍递增Burst |
项目中AHB2APB接的APB SLAVE依然是上一节用到的cmsdk_apb4_eg_slave。
cmsdk_ahb_to_apb.v
module cmsdk_ahb_to_apb #(
// Parameter to define address width
// 16 = 2^16 = 64KB APB address space
parameter ADDRWIDTH = 16,
parameter REGISTER_RDATA = 1,
parameter REGISTER_WDATA = 0)
(
// --------------------------------------------------------------------------
// Port Definitions
// --------------------------------------------------------------------------
input wire HCLK, // Clock
input wire HRESETn, // Reset
input wire PCLKEN, // APB clock enable signal
input wire HSEL, // Device select
input wire [ADDRWIDTH-1:0] HADDR, // Address
input wire [1:0] HTRANS, // Transfer control
input wire [2:0] HSIZE, // Transfer size
input wire [3:0] HPROT, // Protection control
input wire HWRITE, // Write control
input wire HREADY, // Transfer phase done
input wire [31:0] HWDATA, // Write data
output reg HREADYOUT, // Device ready
output wire [31:0] HRDATA, // Read data output
output wire HRESP, // Device response
// APB Output
output wire [ADDRWIDTH-1:0] PADDR, // APB Address
output wire PENABLE, // APB Enable
output wire PWRITE, // APB Write
output wire [3:0] PSTRB, // APB Byte Strobe
output wire [2:0] PPROT, // APB Prot
output wire [31:0] PWDATA, // APB write data
output wire PSEL, // APB Select
output wire APBACTIVE, // APB bus is active, for clock gating
// of APB bus
// APB Input
input wire [31:0] PRDATA, // Read data for each APB slave
input wire PREADY, // Ready for each APB slave
input wire PSLVERR); // Error state for each APB slave
// --------------------------------------------------------------------------
// Internal wires
// --------------------------------------------------------------------------
reg [ADDRWIDTH-3:0] addr_reg; // Address sample register
reg wr_reg; // Write control sample register
reg [2:0] state_reg; // State for finite state machine
reg [3:0] pstrb_reg; // Byte lane strobe register
wire [3:0] pstrb_nxt; // Byte lane strobe next state
reg [1:0] pprot_reg; // PPROT register
wire [1:0] pprot_nxt; // PPROT register next state
wire apb_select; // APB bridge is selected
wire apb_tran_end; // Transfer is completed on APB
reg [2:0] next_state; // Next state for finite state machine
reg [31:0] rwdata_reg; // Read/Write data sample register
wire reg_rdata_cfg; // REGISTER_RDATA paramater
wire reg_wdata_cfg; // REGISTER_WDATA paramater
reg sample_wdata_reg; // Control signal to sample HWDATA
// -------------------------------------------------------------------------
// State machine
// -------------------------------------------------------------------------
localparam ST_BITS = 3;
localparam [ST_BITS-1:0] ST_IDLE = 3'b000; // Idle waiting for transaction
localparam [ST_BITS-1:0] ST_APB_WAIT = 3'b001; // Wait APB transfer
localparam [ST_BITS-1:0] ST_APB_TRNF = 3'b010; // Start APB transfer
localparam [ST_BITS-1:0] ST_APB_TRNF2 = 3'b011; // Second APB transfer cycle
localparam [ST_BITS-1:0] ST_APB_ENDOK = 3'b100; // Ending cycle for OKAY
localparam [ST_BITS-1:0] ST_APB_ERR1 = 3'b101; // First cycle for Error response
localparam [ST_BITS-1:0] ST_APB_ERR2 = 3'b110; // Second cycle for Error response
localparam [ST_BITS-1:0] ST_ILLEGAL = 3'b111; // Illegal state
// --------------------------------------------------------------------------
// Start of main code
// --------------------------------------------------------------------------
// Configuration signal
assign reg_rdata_cfg = (REGISTER_RDATA==0) ? 1'b0 : 1'b1;
assign reg_wdata_cfg = (REGISTER_WDATA==0) ? 1'b0 : 1'b1;
// Generate APB bridge select
assign apb_select = HSEL & HTRANS[1] & HREADY;
// Generate APB transfer ended
assign apb_tran_end = (state_reg==3'b011) & PREADY;
assign pprot_nxt[0] = HPROT[1]; // (0) Normal, (1) Privileged
assign pprot_nxt[1] = ~HPROT[0]; // (0) Data, (1) Instruction
// Byte strobe generation
// - Only enable for write operations
// - For word write transfers (HSIZE[1]=1), all byte strobes are 1
// - For hword write transfers (HSIZE[0]=1), check HADDR[1]
// - For byte write transfers, check HADDR[1:0]
assign pstrb_nxt[0] = HWRITE & ((HSIZE[1])|((HSIZE[0])&(~HADDR[1]))|(HADDR[1:0]==2'b00));
assign pstrb_nxt[1] = HWRITE & ((HSIZE[1])|((HSIZE[0])&(~HADDR[1]))|(HADDR[1:0]==2'b01));
assign pstrb_nxt[2] = HWRITE & ((HSIZE[1])|((HSIZE[0])&( HADDR[1]))|(HADDR[1:0]==2'b10));
assign pstrb_nxt[3] = HWRITE & ((HSIZE[1])|((HSIZE[0])&( HADDR[1]))|(HADDR[1:0]==2'b11));
// Sample control signals
always @(posedge HCLK or negedge HRESETn)
begin
if (~HRESETn)
begin
addr_reg <= {(ADDRWIDTH-2){1'b0}};
wr_reg <= 1'b0;
pprot_reg <= {2{1'b0}};
pstrb_reg <= {4{1'b0}};
end
else if (apb_select) // Capture transfer information at the end of AHB address phase
begin
addr_reg <= HADDR[ADDRWIDTH-1:2];
wr_reg <= HWRITE;
pprot_reg <= pprot_nxt;
pstrb_reg <= pstrb_nxt;
end
end
// Sample write data control signal
// Assert after write address phase, deassert after PCLKEN=1
wire sample_wdata_set = apb_select & HWRITE & reg_wdata_cfg;
wire sample_wdata_clr = sample_wdata_reg & PCLKEN;
always @(posedge HCLK or negedge HRESETn)
begin
if (~HRESETn)
sample_wdata_reg <= 1'b0;
else if (sample_wdata_set | sample_wdata_clr)
sample_wdata_reg <= sample_wdata_set;
end
// Generate next state for FSM
// Note : case 3'b111 is not used. The design has been checked that
// this illegal state cannot be entered using formal verification.
always @(state_reg or PREADY or PSLVERR or apb_select or reg_rdata_cfg or
PCLKEN or reg_wdata_cfg or HWRITE)
begin
case (state_reg)
// Idle
ST_IDLE :
begin
if (PCLKEN & apb_select & ~(reg_wdata_cfg & HWRITE))
next_state = ST_APB_TRNF; // Start APB transfer in next cycle
else if (apb_select)
next_state = ST_APB_WAIT; // Wait for start of APB transfer at PCLKEN high
else
next_state = ST_IDLE; // Remain idle
end
// Transfer announced on AHB, but PCLKEN was low, so waiting
ST_APB_WAIT :
begin
if (PCLKEN)
next_state = ST_APB_TRNF; // Start APB transfer in next cycle
else
next_state = ST_APB_WAIT; // Wait for start of APB transfer at PCLKEN high
end
// First APB transfer cycle
ST_APB_TRNF :
begin
if (PCLKEN)
next_state = ST_APB_TRNF2; // Change to second cycle of APB transfer
else
next_state = ST_APB_TRNF; // Change to state-2
end
// Second APB transfer cycle
ST_APB_TRNF2 :
begin
if (PREADY & PSLVERR & PCLKEN) // Error received - Generate two cycle
// Error response on AHB by
next_state = ST_APB_ERR1; // Changing to state-5 and 6
else if (PREADY & (~PSLVERR) & PCLKEN) // Okay received
begin
if (reg_rdata_cfg)
// Registered version
next_state = ST_APB_ENDOK; // Generate okay response in state 4
else
// Non-registered version
next_state = {2'b00, apb_select}; // Terminate transfer
end
else // Slave not ready
next_state = ST_APB_TRNF2; // Unchange
end
// Ending cycle for OKAY (registered response)
ST_APB_ENDOK :
begin
if (PCLKEN & apb_select & ~(reg_wdata_cfg & HWRITE))
next_state = ST_APB_TRNF; // Start APB transfer in next cycle
else if (apb_select)
next_state = ST_APB_WAIT; // Wait for start of APB transfer at PCLKEN high
else
next_state = ST_IDLE; // Remain idle
end
// First cycle for Error response
ST_APB_ERR1 : next_state = ST_APB_ERR2; // Goto 2nd cycle of error response
// Second cycle for Error response
ST_APB_ERR2 :
begin
if (PCLKEN & apb_select & ~(reg_wdata_cfg & HWRITE))
next_state = ST_APB_TRNF; // Start APB transfer in next cycle
else if (apb_select)
next_state = ST_APB_WAIT; // Wait for start of APB transfer at PCLKEN high
else
next_state = ST_IDLE; // Remain idle
end
default : // Not used
next_state = 3'bxxx; // X-Propagation
endcase
end
// Registering state machine
always @(posedge HCLK or negedge HRESETn)
begin
if (~HRESETn)
state_reg <= 3'b000;
else
state_reg <= next_state;
end
// Sample PRDATA or HWDATA
always @(posedge HCLK or negedge HRESETn)
begin
if (~HRESETn)
rwdata_reg <= {32{1'b0}};
else
if (sample_wdata_reg & reg_wdata_cfg & PCLKEN)
rwdata_reg <= HWDATA;
else if (apb_tran_end & reg_rdata_cfg & PCLKEN)
rwdata_reg <= PRDATA;
end
// Connect outputs to top level
assign PADDR = {addr_reg, 2'b00}; // from sample register
assign PWRITE = wr_reg; // from sample register
// From sample register or from HWDATA directly
assign PWDATA = (reg_wdata_cfg) ? rwdata_reg : HWDATA;
assign PSEL = (state_reg==ST_APB_TRNF) | (state_reg==ST_APB_TRNF2);
assign PENABLE = (state_reg==ST_APB_TRNF2);
assign PPROT = {pprot_reg[1], 1'b0, pprot_reg[0]};
assign PSTRB = pstrb_reg[3:0];
// Generate HREADYOUT
always @(state_reg or reg_rdata_cfg or PREADY or PSLVERR or PCLKEN)
begin
case (state_reg)
ST_IDLE : HREADYOUT = 1'b1; // Idle
ST_APB_WAIT : HREADYOUT = 1'b0; // Transfer announced on AHB, but PCLKEN was low, so waiting
ST_APB_TRNF : HREADYOUT = 1'b0; // First APB transfer cycle
// Second APB transfer cycle:
// if Non-registered feedback version, and APB transfer completed without error
// Then response with ready immediately. If registered feedback version,
// wait until state_reg==ST_APB_ENDOK
ST_APB_TRNF2 : HREADYOUT = (~reg_rdata_cfg) & PREADY & (~PSLVERR) & PCLKEN;
ST_APB_ENDOK : HREADYOUT = reg_rdata_cfg; // Ending cycle for OKAY (registered response only)
ST_APB_ERR1 : HREADYOUT = 1'b0; // First cycle for Error response
ST_APB_ERR2 : HREADYOUT = 1'b1; // Second cycle for Error response
default: HREADYOUT = 1'bx; // x propagation (note :3'b111 is illegal state)
endcase
end
// From sample register or from PRDATA directly
assign HRDATA = (reg_rdata_cfg) ? rwdata_reg : PRDATA;
assign HRESP = (state_reg==ST_APB_ERR1) | (state_reg==ST_APB_ERR2);
assign APBACTIVE = (HSEL & HTRANS[1]) | (|state_reg);
endmodule
下面波形中两条蓝线之间是一次NON SEQ的读传输。上半部分是AHB Slave信号,下半部分是APB Master信号。
从第一根蓝线开始算第一个Clock
下面波形中两条蓝线之间是一次NON SEQ的写传输。上半部分是AHB Slave信号,下半部分是APB Master信号。
从第一根蓝线开始算第一个Clock
AXI一共有五个channel。
写channel有AW, W和B。读channel有AR和R
本文的所有表都是以32 位的数据总线、4 位的写数据闸门、4 位的ID 段。
信号 | 源 | 描述 |
---|---|---|
ACLK | Clock source | 全局时钟信号 |
ARESETn | Reset source | 全局复位信号,低电平有效 |
信号 | 源 | 描述 |
---|---|---|
AWID[3:0] | Master | 写地址 ID,这个信号是写地址信号组的ID tag |
AWADDR[31:0] | Master | 写地址 |
AWLEN[3:0] | Master | Burst len,此信号决定Burst传输的数据的个数 |
AWSIZE[2:0] | Master | Burst的大小 |
AWBURST[1:0] | Master | Burst类型 |
AWLOCK[1:0] | Master | Lock类型 |
AWCACHE[3:0] | Master | Cache类型 |
AWPROT[2:0] | Master | 保护类型 |
AWVALID | Master | 写地址有效 1= 地址和控制信息有效 0 = 地址和控制信息无效 这个信号会一直保持,直到AWREADY为高 |
AWREADY | Slave | 写地址准备好,这个信号用爱来指明设备已经准备好接受地址和控制信息了。 1=设备准备好 0=设备没准备好 |
信号 | 源 | 描述 |
---|---|---|
WID[3:0] | Master | 写 ID tag,WID 的值必须与AWID 的值匹配 |
WDATA[31:0] | Master | 写的数据 |
WSTRB[3:0] | Master | 写阀门。WSTRB[n]标示的区间为WDATA[(8n)+7:(8n)] |
WLAST | Master | 写的最后一个数据 |
WVALID | Master | 写有效 |
WREADY | Slave | 写就绪 |
信号 | 源 | 描述 |
---|---|---|
BID[3:0] | Slave | 响应ID,WID 的值必须与AWID 的值匹配 |
BRESP[3:0] | Slave | OKAY, EXOKAY, SLVERR, DECERR |
BVALID | Slave | 写响应有效 |
BREADY | Master | 主机响应就绪 |
信号 | 源 | 描述 |
---|---|---|
ARID[3:0] | Master | 读地址 ID。 |
ARADDR[31:0] | Master | 读地址。 |
ARLEN[3:0] | Master | Burst长度 |
ARSIZE[2:0] | Master | Burst大小 |
ARBURST[1:0] | Master | Burst类型 |
ARLOCK[1:0] | Master | 锁类型 |
ARPROT[2:0] | Master | 保护类型 |
ARVALID | Master | 读地址有效 |
ARREADY | Slave | 读地址就绪 |
信号 | 源 | 描述 |
---|---|---|
RID[3:0] | Slave | 读ID tag |
RDATA[31:0] | Slave | 读数据 |
RRESP[1:0] | Slave | 读响应 |
RLAST | Slave | 最后一个数据 |
RVALID | Slave | 读数据有效 |
RREADY | Master | 读数据就绪 |
当地址出现在地址总线后,传输的数据将出现在读数据通道上。设备保持VALID
为低直到读数据有效。为了表明一次突发式读写的完成,设备用RLAST 信号来
表示最后一个被传输的数据。
设备会在第一次突发式读完成后处理第二次突发式读数据。也就意味着,主机一
开始传送了两个地址给设备。设备在完全处理完第一个地址的数据之后才开始处
理第二个地址的数据。
这一过程的开始时,主机发送地址和控制信息到写地址通道中,然后主机发送每
一个写数据到写数据通道中。当主机发送最后一个数据时,WLAST 信号就变为
高。当设备接收完所有数据之后他将一个写响应发送回主机来表明写事务完成。
15、AXI 协议支持乱序传输。他给每一个通过接口的事务一个IDtag。协议要求
相同ID tag 的事务必须有序完成,而不同ID tag 可以乱序完成。
AXI2SRAM 是将AXI信号转换成SRAM信号。因为AXI是读写能同时进行的,而SRAM信号只能读或者写,所以AXI到SRAM的信号转换需要进行读写仲裁。在本例子中采用round-robin方式去仲裁AXI的读写,因为对于SRAM来说,读写的概率是一半一半。默认情况下是读。
IntMemAxi.v
`timescale 1ns / 1ps
module IntMemAxi (
//Global Signals
ACLK,
ARESETn,
//WA Channel
AWID,
AWADDR,
AWLEN,
AWSIZE,
AWBURST,
AWVALID,
AWREADY,
//W Channel
WSTRB,
WLAST,
WVALID,
WREADY,
//B Channel
BID,
BRESP,
BVALID,
BREADY,
//AR Channel
ARID,
ARADDR,
ARLEN,
ARBURST,
ARVALID,
ARREADY,
//R Channel,
RID,
RRESP,
RLAST,
RVALID,
RREADY,
//Memory Interface
MEMCEn,
MEMWEn,
MEMADDR,
//Dummmy Scan Pins
SCANENABLE,
SCANINACLK,
SCANOUTACLK
);
parameter DATA_WIDTH = 64;
parameter ID_WIDTH = 4;
parameter NUM_RD_WD = 1'b0;
parameter IS_ROM = 1'b0;
parameter STRB_WIDTH = DATA_WIDTH / 8;
parameter STRB_MAX = STRB_WIDTH - 1;
parameter ID_MAX = ID_WIDTH - 1;
input ACLK;
input ARESETn;
input [ID_MAX:0] AWID;
input [31:0] AWADDR;
input [3:0] AWLEN;
input [2:0] AWSIZE;
input [1:0] AWBURST;
input AWVALID;
output AWREADY;
input [STRB_MAX:0] WSTRB;
input WLAST;
input WVALID;
output WREADY;
output [ID_MAX:0] BID;
output [1:0] BRESP;
output BVALID;
output BRESP;
input [ID_MAX:0] ARID;
input [31:0] ARADDR;
input [3:0] ARLEN;
input [1:0] ARBURST;
input [2:0] ARSIZE;
input ARVALID;
output ARREADY;
output [ID_MAX:0] RID;
output [1:0] RRESP;
output RLAST;
output RVALID;
input RREADY;
output MEMCEn;
output [STRB_MAX:0] MEMWEn;
output [31:0] MEMADDR;
input SCANENABLE;
input SCANINACLK;
output SCANOUTACLK;
`include "Axi.v"
// I/O signals
wire ACLK; // clock input
wire ARESETn; // reset async input active low
wire [ID_MAX:0] AWID; //write address ID_MAX
wire [31:0] AWADDR; //address
wire [3:0] AWLEN; //burst length
wire [2:0] AWSIZE; //burst size
wire [1:0] AWBURST; //burst type
wire AWVALID; //address valid
wire WLAST; //write last
wire WVALID; //write valid
wire WREADY; //write ready
reg [ID_MAX:0] BID; //write response id
reg [1:0] BRESP; //write response
wire BVALID; //response valid
wire BREADY; //response ready
wire [ID_MAX:0] ARID; //read response ID
wire [31:0] ARADDR; //address
wire [3:0] ARLEN; //burst length
wire [2:0] ARSIZE; //burst size
wire [1:0] ARBURST; //burst type
wire [ID_MAX:0] RID; //read data id
wire RLAST; //read last
wire [1:0] RRESP; //read response
wire RVALID; //read valid
wire RREADY; //read ready
wire MEMCEn; //memory chip enable
wire [STRB_MAX:0] MEMWEn; //memory write enable
wire [31:0] MEMADDR; //memory address
//Internal signals
wire WAddrNew; //indicates a new AXI write addr received
wire RAddrNew; //indicates a new AXI read addr received
reg WriteSel; //high for a write address, low for read
reg WriteSelNxt; //D-input of WriteSel register
wire ArbEn; //enable for arbiter register
reg WsCount; //wait state decrementor
wire WsCountNxt; //D-input of WsCount register
wire RvalidEn; //indicates when RVALID can legally go high
reg RvalidEnReg; //registered RvalidEn
reg [ID_MAX:0] AridReg; //ARID held througout a read burst
//Output register D-inputs
wire WreadyNxt;
wire BvalidNxt;
wire [ID_MAX:0] RidNxt;
wire RvalidNxt;
wire RlastNxt;
//Unpacked address buses
wire [31:0] MemAwAddr; //write burst address
wire MemWLast; //high for last address of write burst
wire MemAwValid; //valid write address from IntMemUnpackAddr
wire MemAwReady; //accepts write address from IntMemUnpackAddr
wire [31:0] MemArAddr; //read burst address;
wire MemRlast; //high for last address of read burst
wire MemArValid;//valid address from IntMemUnpackAddr
wire MemArReady;//accepts read address from IntMemUnpackAddr
//Internal versions of output signals
wire iAWREADY; //internal version of AWREADY
wire iARREADY;
reg iWREADY;
reg iBVALID;
reg [ID_MAX:0] iRID;
reg iRVALID;
reg iRLAST;
wire iMemCEn;
// =============================================================
// Main body of code
// =============================================================
//------------------------------------------------------
//Address Arbiter
//------------------------------------------------------
//Arbitration between read and write addressses is round-robin
//This implementation waits for valid write address and data before granting
//the write channel
//Use MemAxValid signals to help timing, could use ARVALID & AWVALID to save
//an arbitration cycle.
always @(MemArValid or MemAwValid or WVALID or WriteSel)
begin
case ({MemAwValid & WVALID, MemArValid})
2'b00: WriteSelNxt = WriteSel; //no address valid
2'b01: WriteSelNxt = 1'b0; //read address valid
2'b10: WriteSelNxt = 1'b1; //write address adn data valid
2'b11: WriteSelNxt = ~WriteSel; //both valid use round-robin scheme
default: WriteSelNxt = 1'bx; //invalid case - progagate x
endcase
end
//Arbitration can only change when the selected channel is idle or when a burst is complete
assign ArbEn = (WriteSel & //write channel granted
~(MemAwValid | //no valid write address or
(MemWLast & MemAwReady))) | //last write data received
(~WriteSel //read channel granted
& ~RvalidEn) //read in progress
//Address arbiter register
always @ (posedge ACLK or negedge ARESETn)
begin : p_WriteSelReg
if (!ARESETn)
WriteSel <= 1'b0; //default is to accept read address
else
if (ArbEn)
WriteSel <= WriteSelNxt;
end
//------------------------------------------------------
//Address Unpackers
//------------------------------------------------------
//This block generates addresses for each beat of the burst/
//Seperate block used for read and write addressses to enable the IntMemAxi
//interface to keep ARREADY and AWREADY both by default
//Write address unpacker
IntMemUnpackAddr uAwIntMemUnpackAddr
(
.ALCK (ACLK),
.ARESETn (ARESETn),
.ADDR (AWADDR),
.ASIZE (AWSIZE),
.ABURST (AWBURST),
.ALEN (AWLEN),
.AVALID (AWVALID),
.AREADY (iAWREADY),
.AddrOut (MemAwAddr), //Unpacked address
.AddrLast (MemWLast), //high for the last address of the burst
.AddrValid (MemAwValid), //high for a valid address
.AddrReady (MemAwReady),
);
//drive output from internal version
assign AWREADY = iAWREADY;
//MemAwReady indicates when the unpacked write address is used.
//Waits for write response to be accepted before accepting the last address
assign MemAwReady = ((WVALID & ~MemWLast &iWREADY) |
//non-last write date accept
(iBVALID & BREADY));
//Write response accept
//Read address unpacker
IntMemUnpackAddr uAwIntMemUnpackAddr
(
.ALCK (ACLK),
.ARESETn (ARESETn),
.ADDR (ARADDR),
.ASIZE (ARSIZE),
.ABURST (ARBURST),
.ALEN (ARLEN),
.AVALID (ARVALID),
.AREADY (iARREADY),
.AddrOut (MemArAddr), //Unpacked address
.AddrLast (MemRLast), //high for the last address of the burst
.AddrValid (MemArValid), //high for a valid address
.AddrReady (MemArReady),
);
assign AREADY = iARREADY;
//MemArReady indicates when the unpacked read address is used
assign MemArReady = ~iMemCEn & ~WriteSel
//------------------------------------------------------
//Wait state counter
//------------------------------------------------------
assign WsCountNxt = ~WriteSel & ~iMemCEn ? NUM_RD_WS :
//load on new read transfer
(iRVALID & RREADY) ? 1'b0 :
//zero when read data transfered
RvalidEn & ~iRVALID ? WsCount - 1:
//in read transfer, so dcrement
WsCount; // otherwise keep stable
//WsCount register
always @ (posedge ACLK or negedge ARESETn)
begin : p_WsCountSeq
if (!ARESETn)
WsCount <= 1'b0;
else
WsCount <= WsCountNxt;
//------------------------------------------------------
//Output Signals
//------------------------------------------------------
//------------------------------------------------------
//Write channel outputs
//------------------------------------------------------
//WREADY goes high when a valid write address is seleceted but not wating for
//write response to be accepted
assign WreadyNxt = (WriteSel | (WriteSelNxt & ArbEn)) & //write granted
MemAwValid & //valid write addr accpeted
~(WVALID & WLATS & iWREADY) & //not last write data
~iBVALID; //not wating for BREADY
//WREADY ouput register
always @ (posedge ACLK or negedge ARESETn)
begin: p_WreadyReg
if (!ARESETn)
iWREADY <= 1'b0;
else
iWREADY <= WreadyNxt;
end
//------------------------------------------------------
//Write Response channel outputs
//------------------------------------------------------
//BVALID is calculated a cycle in advance
assign BvalidNxt = WVALID & MemWLast & iWREADY ? 1'b1 :
//goes high when last write data accpeted
BREADY ? 1'b0 : //goes low when response accepted
iBVALID;
//BVALID output register
always @(posedge ACLK or negedge ARESETn)
begin: p_BvalidReg
if (!ARESETn)
iBVALID <= 1'b0;
else
iBVALID <= BvalidNxt;
end
//assign output from internal version
assign BVALID = iBVALID
//BRESP always returns okay if configuraed as a RAM interface and error
//if configuraed as a ROM interface
assign BRESP = (IS_ROM == 1'b1) ? `AXI_RESP_SLVERR : `AXI_RESP_OKAY;
//New write address accepted
assign WAddrNew = AWVALID & iAWREADY;
//BID is registered from AWID when an address is accepted
always @ (posedge ACLK or negedge ARESETn)
begin: p_BidReg
if (!ARESETn)
BID <= {ID_WIDTH{1'b0}};
else
if (WAddrNew)
BID <= AWID;
end
//------------------------------------------------------
//Read Response channel outputs
//------------------------------------------------------
//RvalidEn is a flag which is set when a read address is samp;led by the
//memory and is reset when the read datra is presented onto the AXI read bus.
//This enables support for read wait states.
assign RvalidEn = (~iMemCEn & MemArValid & ~WriteSel) ? 1'b1:
iRVALID & RREADY ? 1'b0 :
RvalidEnReg;
//Read data is pipelined, RVALID goes high after memory has sampled MEMCEn
//and correct number of wait states have been inserted
assign RvalidNxt = ((RvalidEn & WsCountNxt == 1'b0) | (iRVALID & ~RREADY));
//RVALID register
always @(posedge ACLK or negedge ARESETn)
begin: p_RvalidReg
if (!ARESETn)
begin
iRVALID <= 1'b0;
RvalidEnReg <= 1'b0;
end
else
begin
iRVALID <= RvalidNxt;
RvalidEnReg <= RvalidEn;
end
end
//assign output from internal version
assign RVALID = iRVALID;
//RRESP always returns OKAY
assign RRESP = `AXI_RESP_OKAY;
//RLAST driven from MemRlast
assign RlastNxt = ~iMemCEn & MemArValid ? MemRLast :
//RLAST sampled on memory read
iRLAST; //otherwise keep stabl
//RLAST ouput register
always @(posedge ACLK or negedge ARESETn)
begin:
if(!ARESETn)
iRLAST <= 1'b0;
else
iRLAST <= RlastNxt;
end
//drive RLAST from internal version
assign RLAST = iRLAST;
//New read address accepted
assign RAddrNew = ARVALID & iARREADY;
//RID is registerd from ARID when an address is accepted
always @(posedge ACLK or negedge ARESETn)
begin: p_AridReg
if (!ARESETn)
AridReg <= {ID_WIDTH{1'b0}};
else
if (RAddrNew)
AridReg <= ARID;
end
//RID is driven from the stored ARID except when waiting for RREADY high,
//When it must be kept stable
assign RidNxt = iRVALID & ~RREADY ? iRID:
MemArValid ? AridReg :
iRID;
//RID ouput register
always @ (posedge ACLK or negedge ARESETn)
begin: p_RidReg
if (!ARESETn)
iRID <= {ID_WIDTH{1'b0}};
else
iRID <= RidNxt;
end
assign RID = iRID;
//------------------------------------------------------
//Memory interface
//------------------------------------------------------
//select between address channels based on read/write channel arbitration
assign MEMADDR = WriteSel ? MemAwAddr : //write granted, use write address
MemArAddr; //otherwise use read address
//Active low chip enable asserted when valid transfer is on memory interface
assign iMemCEn = (~WriteSel & MemArValid & WsCount == 1'b0 &
//read granted and valid read address and
~(iRVALID & iRLAST) &
//not last read transfer and
~(iRVALID & ~RREADY) ? 1'b0 :
//not waiting for RREADY high then asserted
(WVALID & iWREADY) ? 1'b0 :
//assert when valid write data accpeted
1'b1);
//assign output from internal version
assign MEMCEn = iMemCEn;
//active low write enable is driven from WSTRB when write is granted
assign MEMWEn = ~(WSTRB & {STRB_WIDTH{WriteSel}});
endmodule
IntMemUnpackAddr.v
`timescale 1ns/1ps
`include "Axi.v"
module IntMemUnpackAddr
(
// Global
ACLK,
ARESETn,
// AXI interface
ADDR,
ASIZE,
ABURST,
ALEN,
AVALID,
AREADY,
//Unpacked address interface
AddrOut, //incrementing address
AddrLast, //indicates last address in burst
AddrValid, //incrementing address is valid
AddrReady, //address accepted
);
input ACLK;
input ARESETn;
input [31:0] ADDR;
input [2:0] ASIZE;
input [1:0] ABURST;
input [3:0] ALEN;
input AVALID;
output AREADY;
output [31:0] AddrOut;
output AddrLast;
output AddrValid;
input AddrReady;
wire [31:0] ADDR;
wire [2:0] ASIZE;
wire [3:0] ABURST;
wire [1:0] ALEN;
wire AVALID;
wire AREADY;
wire [31:0] AddrOut;
wire AddrLast;
wire AddrValid;
wire AddrReady;
// internal version of output signals
reg [31:0] iAddrOut;
reg iAddrLast;
reg iAddrValid;
wire iAREADY;
// internal signals
wire [31:0] AddrOutNxt;
wire AddrLastNxt;
wire AddrValidNxt;
wire [11:0] AddrIncr;
reg [3:0] AddrCount;
wire [3:0] AddrCountNxt;
wire AddrNew;
reg [2:0] SizeReg;
reg [1:0] BurstReg;
reg [3:0] LenReg;
//--------------------------------------------
//Main body of code
//--------------------------------------------
//--------------------------------------------
//Instantiation of combinatorial address incrementer
//--------------------------------------------
// As bursts cannot cross 4KB boundary, only least significant 12 bits need to increment
IntMemAddrGen uIntMemAddrGen (
.AddrIn (iAddrOut[11:0]),
.ALEN (LenReg),
.ASIZE (SizeReg),
.ABURST (BurstReg),
.AddrOut (AddrIncr)
);
//--------------------------------------------
//Control signals registers
//--------------------------------------------
// Enable for address registers
assign AddrNew = AVALID & iAREADY;
//Address registers
always @(posedge ACLK or negedge ARESETn)
begin : p_BurstCtrlSeq
if (!ARESETn)
begin
SizeReg <= 3'b000;
BurstReg <= 2'b000;
LenReg <= 4'h0;
end
else
if (AddrNew)
begin
SizeReg <= ASIZE;
BurstReg <= ABURST;
LenReg <= ALEN;
end
end
end
//--------------------------------------------
//AddrOut logic
//--------------------------------------------
//Multiplexors for next address
assign AddrOutNxt = AddrNew ? ADDR :
AddrReady ? {iAddrOut[31:12], AddrIncr} :
iAddrOut;
always @ (posedge ACLK or negedge ARESETn)
begin : p_AddrRegSeq
if (!ARESETn)
iAddrOut <= {32{1'b0}};
else
iAddrOut <= AddrOutNxt;
end
assign AddrOut = iAddrOut;
//--------------------------------------------
//AddrLast logic
//--------------------------------------------
//AddrLast is high when the last address in a burst is presented
//on the address interface
//Count incremented addresses to determine when burst is complete
assign AddrCountNxt = AVALID & iAREADY ? ALEN:
//new burst started, load from ALEN
iAddrValid & AddrReady ? AddrCount - 4'h1:
//incrementing address accepted so decrement
AddrCount;
//Otherwise keep stable
// AddrLast is high whenever the beat counter is zero
assign AddrLastNxt = (AddrCountNxt == 4'h0);
//AddrLast registers
always @ (posedge ACLK or negedge ARESETn)
begin: p_CountReg
if (!ARESETn)
begin
AddrCount <= 4'h0;
iAddrLast <= 1'b0;
end
else
begin
AddrCount <= AddrCountNxt;
iAddrLast <= AddrLastNxt;
end
end
//assign output from internal version
assign AddrLast = iAddrLast;
//--------------------------------------------
//AddrValid logic
//--------------------------------------------
//AddrValid is high when an address is accpeted and goes low when the
//last address is accepted, unless another burst is started
assign AddrValidNxt = AVALID & iAREADY ? 1'b1 :
AddrReady & iAddrLast ? 1'b0 :
iAddrValid;
//AddrValid registers
always @ (posedge ACLK or negedge ARESETn)
begin: p_AddrValidReg
if (!ARESETn)
iAddrValid <= 1'b0;
else
iAddrValid <= AddrValidNxt;
end
assign AddrValid = iAddrValid;
//--------------------------------------------
//READY logic
//--------------------------------------------
//A new AXI address can be accepted when no valid address is stored
assign iAREADY = ~iAddrValid;
assign AREADY = iAREADY;
endmodule
```c
IntMemAddrGen.v
```c
`timescale 1ns / 1ps
`include "Axi.v"
module IntMemAddrGen
(
AddrIn,
ALEN,
ASIZE,
AddrOut
);
input [11:0] AddrIn;
input [3:0] ALEN;
input [2:0] ASIZE;
input [1:0] ABURST;
output [11:0] AddrOut;
wire [11:0] AddrIn;
wire [3:0] ALEN;
wire [2:0] ASIZE;
wire [1:0] ABURST;
wire [11:0] AddrOut;
//-------------------------------------------------
//Signal declarations
//-------------------------------------------------
reg [11:0] OffsetAddr; //shifted address
wire [11:0] IncrAddr; //incremented address
wire [11:0] WrapAddr; //wrapped address
wire [11:0] MuxAddr; //address selected by burst type
reg [11:0] CalcAddr; //final calculated address
//-------------------------------------------------
//Main body of code
//-------------------------------------------------
//-------------------------------------------------
//Combinational address shift right
//OffsetAddr indicates the address bits of interest,
//depending on ASIZE
//-------------------------------------------------
always @ (ASIZE or AddrIn)
begin: p_OffsetAddrComb
case(ASIZE)
`AXI_ASIZE_8 : OffsetAddr = AddrIn[11:0];
`AXI_ASIZE_16 : OffsetAddr = {1'b0, AddrIn[11:1]};
`AXI_ASIZE_32 : OffsetAddr = {2'b00, AddrIn[11:2]};
`AXI_ASIZE_64 : OffsetAddr = {3'b000, AddrIn[11:3]};
default : OffsetAddr = {12{1'bx}};
endcase
end
//-------------------------------------------------
//Address wrappiong
//The address of the next transfer should wrap on the next
//transfer if the boundary is reached. Assume ASIZE = 2,4,8,16
//-------------------------------------------------
assign WrapAddr[11:4] = OffsetAddr[11:4];
assign WrapAddr[3:0] = (ALEN & IncrAddr[3:0]) | (~ALEN & OffsetAddr[3:0])
assign MuxAddr = ABURST == `AXI_ABURST_WRAP ? WrapAddr : IncrAddr;
always @ (ASIZE or AddrIn)
begin: p_ClacAddrComb
case(ASIZE)
`AXI_ASIZE_8 : OffsetAddr = MuxAddr
`AXI_ASIZE_16 : OffsetAddr = {MuxAddr[10:0], 1'b0};
`AXI_ASIZE_32 : OffsetAddr = {MuxAddr[9:0], 2'b00};
`AXI_ASIZE_64 : OffsetAddr = {MuxAddr[8:0], 3'b000};
default : OffsetAddr = {12{1'bx}};
endcase
end
assign AddrOut = (ABURST == `AXI_ABURST_FIXED) ? AddrIn : CalcAddr;
endmodule
AXI.v
//-------------------------------------
//AXI Constants
//-------------------------------------
//ALEN Encoding
`define AXI_LEN_1 4'b0000
`define AXI_LEN_2 4'b0001
`define AXI_LEN_3 4'b0010
`define AXI_LEN_4 4'b0011
`define AXI_LEN_5 4'b0100
`define AXI_LEN_6 4'b0101
`define AXI_LEN_7 4'b0110
`define AXI_LEN_8 4'b0111
`define AXI_LEN_9 4'b1000
`define AXI_LEN_10 4'b1001
`define AXI_LEN_11 4'b1010
`define AXI_LEN_12 4'b1011
`define AXI_LEN_13 4'b1100
`define AXI_LEN_14 4'b1101
`define AXI_LEN_15 4'b1110
`define AXI_LEN_16 4'b1111
//ASIZE Encoding
`define AXI_ASIZE_8 3'b000
`define AXI_ASIZE_16 3'b001
`define AXI_ASIZE_32 3'b010
`define AXI_ASIZE_64 3'b011
`define AXI_ASIZE_128 3'b100
`define AXI_ASIZE_256 3'b101
`define AXI_ASIZE_512 3'b110
`define AXI_ASIZE_1024 3'b111
//ABURST Encoding
`define AXI_ABURST_FIXED 2'b00
`define AXI_ABURST_INCR 2'b01
`define AXI_ABURST_WRAP 2'b10
//ALOCK Encoding
`define AXI_ALOCK_NOLOCK 2'b00
`define AXI_ALOCK_EXEL 2'b01
`define AXI_ALOCK_LOCKED 2'b10
//RRESP / BRESP Encoding
`define AXI_RESP_OKAY 2'b00
`define AXI_RESP_EXOKAY 2'b01
`define AXI_RESP_SLVERR 2'b10
`define AXI-RESP_DEFCERR 2'b11
可以看到,在第一个clock cycle,AR Channel上AREADY, AVALID准备好,同时将地址传送到AXI Bus上。从ARLEN,ARSIZE算出,每一笔burst要传输8 * 8 个byte。每一笔transaction传输8个byte,一共有8笔transaction。从第三个clock cycle开始,从SRAM读8个64 bit的数据给RDATA。当最后一个transaction完成后,AXI2SRAM将RLAST拉高表示burst传输完成。这里没有error,所以一直将RRESP拉低。这就是一次有等待周期的读传输。
写时序与读时序类似。第一个clock cycle采样AW Channel上的信号,然后等待一个周期,开始对SRAM进行写操作。同样的,写了8笔64 bit的数据到SRAM上。当写到0x1038的时候,讲WLAST拉高,表示最后以后写数据。当传输完成这一笔burst后,讲BVALID表示此次写操作完成。同样的,这里没有error,所以BRESP为0既可。