AMBA协议学习小记

AMBA

APB

APB2接口

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

AMBA协议学习小记_第1张图片

读时序

  • PWRITE为0时表示读数据
  • T0时刻PSEL为0,没有APB Bus上的操作
  • T1时刻PSEL为1,PENABLE为0,此时为setup阶段,APB Bus上传输要读的地址
  • T2时刻PSEL为1,PENABLE为1,此时为access阶段,APB Bus上传输读出的数据Data1,在这个时刻数据返回。

写时序

  • PWRITE为1时表示写数据
  • T0时刻PSEL为0,没有APB Bus上的操作
  • T1时刻PSEL为1,PENABLE为0,此时为setup阶段,APB Bus上传输要写的地址
  • T2时刻PSEL为1,PENABLE为1,此时为access阶段,APB Bus上传写入的数据Data1

由于APB只要操作register,所以APB操作只有两拍足够。 APB3和APB4两拍是不够的

APB3接口

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的写时序为例

  • 在setup阶段后面PREADY拉低了,这个时候表示IP还没准备好,数据不会写入
  • 再过了一拍之后,PREADY拉高,IP准备好了,这个时候APB3 Bus上的数据会写入到相应寄存器。

什么情况下产生Error response?(PSLVERR)

APB的外设可以选择产生或者不产生error response,如果不产生的话,只需要把连接APB外设的模块(比如APB bridge)对应PSLVERR端口接0即可

在以下情况下,APB的模块需要产生error response:

  1. 在访问APB外设的寄存器的时候,APB外设在功能设计的时候,认为此时应该产生error,具体情况要取决于外设的设计
  2. 对于APB4的外设,如果能支持secure的话,当外设的secure寄存器被non-secure的transfer访问的时候,这时候需要产生error
  3. 如果APB对unused(没有分配地址空间)的地址空间访问时候,这时候需要产生error。

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
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

AMBA协议学习小记_第2张图片

APB Demo

AMBA协议学习小记_第3张图片

下面是一个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。

代码实现

顶层模块 cmsdk_apb4_eg_slave

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

cmsdk_apb4_eg_slave_interface

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

cmsdk_apb4_eg_slave_reg

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

AMBA协议学习小记_第4张图片

上图是ahb_interface上的波形,第一次红的地方就是写数据到IP中,第二次蓝的地方就是从IP register中读数据出来,波形跟AMBA手册上的波形一模一样。

AMBA协议学习小记_第5张图片

AHB

AHB-Lite Master信号

AHB-Lite Master提供地址和控制信息来标识读写操作。图1-2显示了一个AHB-Lite Master接口。
AMBA协议学习小记_第6张图片

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信号

AMBA协议学习小记_第7张图片

AHB-Lite Slave接口
Slave的接口正好与Master相反,只是Slave多了一个HSELx信号,该信号是从AHB仲裁器用来选择哪个Slave的信号

input HSELx		//每个AHB-Lite Slave拥有自己的选择信号HSELx a,该信号表示当前传输的传输对象。
				//当Slave被选中,它就必须监视HREADY信号,以确保在前一个总线传输完成后,马上开始当前传输
input HREADY	//当为高时,提示Master和所有的Slave,先前的传输完成。

AHB传输时序

读时序

Master在Address phase上驱动HADDR,Slave等待若干个Clock之后送出data,在等待期间,Slave将HREADY信号拉低,SLAVE在完成传输后,将HREADY拉高,此时Master采样得到Data(A)。若无等待周期,则Slave在Address phase后面一个clock给出数据,并直接将HREADY 拉高。

AMBA协议学习小记_第8张图片

写时序

写时序与读书须差不多,Master在Address phase送出地址,在Data phase将HWRITE拉高,等待Slave的HREADY拉高,否则Master移知将HWDATA维持在Data(A)。直到接受到Slave的HREADY拉高,表明一次写传输完成。
AMBA协议学习小记_第9张图片

HTRANS

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,则地址回卷。

AMBA协议学习小记_第10张图片

HSIZE

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。

HBRUST

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

WRAP4 Burst
AMBA协议学习小记_第11张图片

INCR4 BURST
AMBA协议学习小记_第12张图片

INCR BURST
AMBA协议学习小记_第13张图片

AHB2APB Bridge

AMBA协议学习小记_第14张图片

AMBA协议学习小记_第15张图片

AMBA协议学习小记_第16张图片
AMBA协议学习小记_第17张图片
AMBA协议学习小记_第18张图片

AMBA协议学习小记_第19张图片

AMBA协议学习小记_第20张图片

AHB2APB Bridge代码。

项目中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

  • 第一个HCLK,此时AHB2APB Bridege状态是ST_APB_WAIT状态,此时采样HADDR为0xfd4,HTRANS为NON_SEQ传输,HSIZE为word,一笔数据传输为一个word。下一个状态是ST_APB_TRNF。
  • 第二个HCLK,此时AHB2APB Bridege状态是ST_APB_TRNF状态, APB Slave还没有数据传过来,所以AHB2APB插入一些等待周期,将HREADYOUT拉低,同时在这个时候把PSEL拉高,并且把HADDR上的地址送给PADDR上。
  • 第三个HCLK,此时AHB2APB Bridege状态是ST_APB_TRNF状态, 由于PCLK是HCLK的1/2,所以对于APB的操作的状态会延长一倍。这时迎来第二个PCLK的上升沿,此时对应于APB来说是就是SETUP Phase。
  • 第四个HCLK, 此时AHB2APB Bridege状态是ST_APB_TRNF2状态, 拉高PENABLE信号,等待下一个PCLK上升沿。
  • 第五个HCLK,此时AHB2APB Bridege状态是ST_APB_TRNF2状态。此时对应于APB来说是就是ENABLE Phase。这个时候APB Slave会将数据驱动到PRDATA数据线上。对于APB总线操作在此刻就完成。
  • 第六个HCLK,此时AHB2APB Bridege状态是ST_APB_TRNF2状态ST_APB_ENDOK,这个时候AHB2APB Bridge将锁存的PDATA驱动到HRDATA上,并拉高HREADYOUT。到这里一个完整AHB的读操作就完成了。
    从上述描述可以看到,从APB发起读数据到读到数据用了6个HCLK。中间四个HCLK用于对APB的操作,所以在这四个HCLK,AHB2APB需要插入等待周期。就是将HREADYOUT拉低。
    AMBA协议学习小记_第21张图片

下面波形中两条蓝线之间是一次NON SEQ的写传输。上半部分是AHB Slave信号,下半部分是APB Master信号。
从第一根蓝线开始算第一个Clock

  • 第一个HCLK,此时AHB2APB Bridege状态是ST_APB_WAIT状态,此时采样HADDR为0x8,HTRANS为NON_SEQ传输,HSIZE为word,一笔数据传输为一个word。下一个状态是ST_APB_TRNF。此时HWRITE为高,代表这是一次写操作。
  • 第二个HCLK,此时AHB2APB Bridege状态是ST_APB_TRNF状态, 第二个PCL还没有到,所以AHB2APB插入一些等待周期,将HREADYOUT拉低,同时在这个时候把PSEL拉高,并且把HADDR上的地址送给PADDR上。
  • 第三个HCLK,此时AHB2APB Bridege状态是ST_APB_TRNF状态, 由于PCLK是HCLK的1/2,所以对于APB的操作的状态会延长一倍。这时迎来第二个PCLK的上升沿,此时对应于APB来说是就是SETUP Phase,将地址0x8驱动PADDR上。
  • 第四个HCLK, 此时AHB2APB Bridege状态是ST_APB_TRNF2状态, 拉高PENABLE信号,等待下一个PCLK上升沿。
  • 第五个HCLK,此时AHB2APB Bridege状态是ST_APB_TRNF2状态。此时对应于APB来说是就是ENABLE Phase。这个时候AHB2APB Bridge会将数据0x12345678驱动到PWDATA数据线上。对于APB总线操作在此刻就完成,此时数据已经写到APB Slave中。
  • 第六个HCLK,此时AHB2APB Bridege状态是ST_APB_TRNF2状态ST_APB_ENDOK,并拉高HREADYOUT。到这里一个完整AHB的读操作就完成了。
    从上述描述可以看到,从APB发起写数据到写入数据陈宫用了6个HCLK。中间四个HCLK用于对APB的操作,所以在这四个HCLK,AHB2APB需要插入等待周期。就是将HREADYOUT拉低。
    AMBA协议学习小记_第22张图片

AXI

AXI一共有五个channel。
写channel有AW, W和B。读channel有AR和R
AMBA协议学习小记_第23张图片

Read Channel

AMBA协议学习小记_第24张图片

Write Channel

AMBA协议学习小记_第25张图片

  • 这 5 条独立的通道都包含一个信息信号和一个双路的VALD、READY 握手机制。
  • 信息源通过 VALID 信号来指示通道中的数据和控制信息什么时候有效。目标地源用READY 信号来表示何时能够接收数据。读数据和写数据通道都包括一个LAST 信号,用来指明一个事物传输的最后一个数据。
  • 传输地址信息和数据都是在 VALID 和READY 同时为高时有效。

AXI 信号

本文的所有表都是以32 位的数据总线、4 位的写数据闸门、4 位的ID 段。

全局信号

信号 描述
ACLK Clock source 全局时钟信号
ARESETn Reset source 全局复位信号,低电平有效

AW Channel信号

信号 描述
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=设备没准备好

W Channel信号

信号 描述
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 写就绪

B Channel信号

信号 描述
BID[3:0] Slave 响应ID,WID 的值必须与AWID 的值匹配
BRESP[3:0] Slave OKAY, EXOKAY, SLVERR, DECERR
BVALID Slave 写响应有效
BREADY Master 主机响应就绪

AR Channel信号

信号 描述
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 读地址就绪

A Channel信号

信号 描述
RID[3:0] Slave 读ID tag
RDATA[31:0] Slave 读数据
RRESP[1:0] Slave 读响应
RLAST Slave 最后一个数据
RVALID Slave 读数据有效
RREADY Master 读数据就绪

ARLEN/AWLEN

AMBA协议学习小记_第26张图片

ARSIZE/AWSIZE

AMBA协议学习小记_第27张图片

ARBUSRT/AWBURST

AMBA协议学习小记_第28张图片

ARESP/BRESP

AMBA协议学习小记_第29张图片

传输时序

读时序

AMBA协议学习小记_第30张图片
当地址出现在地址总线后,传输的数据将出现在读数据通道上。设备保持VALID
为低直到读数据有效。为了表明一次突发式读写的完成,设备用RLAST 信号来
表示最后一个被传输的数据。
AMBA协议学习小记_第31张图片
设备会在第一次突发式读完成后处理第二次突发式读数据。也就意味着,主机一
开始传送了两个地址给设备。设备在完全处理完第一个地址的数据之后才开始处
理第二个地址的数据。

写时序

AMBA协议学习小记_第32张图片

这一过程的开始时,主机发送地址和控制信息到写地址通道中,然后主机发送每
一个写数据到写数据通道中。当主机发送最后一个数据时,WLAST 信号就变为
高。当设备接收完所有数据之后他将一个写响应发送回主机来表明写事务完成。
15、AXI 协议支持乱序传输。他给每一个通过接口的事务一个IDtag。协议要求
相同ID tag 的事务必须有序完成,而不同ID tag 可以乱序完成。

AXI2SRAM实例

概述

AXI2SRAM 是将AXI信号转换成SRAM信号。因为AXI是读写能同时进行的,而SRAM信号只能读或者写,所以AXI到SRAM的信号转换需要进行读写仲裁。在本例子中采用round-robin方式去仲裁AXI的读写,因为对于SRAM来说,读写的概率是一半一半。默认情况下是读。

  • 读时序

  • 写时序

  • 读写时序
    AMBA协议学习小记_第33张图片

Block Diragram
AMBA协议学习小记_第34张图片
整个设计很简单

  • 对于DATA信号来说,这里没有缓冲,所以直接将AXI的WDATA和RDATA连到SRAM的DATA信号上。
  • AXI2SRAM作为AXI Slave, 需要返回W Channel,B Channel和R Channel的信号。
  • AW Channel 和AR Channel作为输入,AXI2SRAM内部有两个Address unpacker,分别解压读地址和写地址,将ABurst,ALen,Asize解析成SRAM地址。SRAM本身没有Burst的概念,所以要这两个unpacker就是将burst解析一个个的单一地址。然后将地址送Address Aarbiter进行读写仲裁。
源代码

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
仿真波形
读时序仿真AMBA协议学习小记_第35张图片

可以看到,在第一个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拉低。这就是一次有等待周期的读传输。

写时序仿真

AMBA协议学习小记_第36张图片
写时序与读时序类似。第一个clock cycle采样AW Channel上的信号,然后等待一个周期,开始对SRAM进行写操作。同样的,写了8笔64 bit的数据到SRAM上。当写到0x1038的时候,讲WLAST拉高,表示最后以后写数据。当传输完成这一笔burst后,讲BVALID表示此次写操作完成。同样的,这里没有error,所以BRESP为0既可。

你可能感兴趣的:(嵌入式开发,ARM,verilog,AMBA)