Axi协议和verilog实现3-axi的verilog实现

        上面学习了axi总线的及本协议和内容,下面来实现一个axi4接口的双端口ram,但是我们只是实现关键信号。

        该设计为一个AXI4接口的双口SRAM,支持读写最大outstanding数为30。数据位宽为32bit,RAM深度为256。支持burst方式为只能为INCR,burst长度支持1~16。读写设计分离,不会产生访问阻塞。

        为了支持AXI的outstanding特性,在设计从机时,需要使用fifo来缓存各通道的指令和数据。
该FIFO特性如下:同步fifo,宽度32,深度31,带有空满指示信号,空时禁止读取。满时禁止写入。预读取:读数据时钟裸漏,当读使能有效时。当前数据消失,下一笔数据裸露,如此直到数据取走为止。fifo设计不是重点,这里列出一个rtl,但是其他的fifo完全可以替代。

//宽度为32,深度为31   //看似深度为32实则为31,最后一个位置永远不会被操作。
//特性:空了不准读,满了不准写。
module sync_fifo(
	input rst_n,
	input clk,

	input wr_en,
	input [31:0]din,
	
	input rd_en,
        output  [31:0]dout,	

	output reg fifo_empty,
	output reg fifo_full
	
);

reg [31:0] mem [31:0];
reg [31:0] w_ram_line; 
reg [$clog2(32)-1:0] ram_pointer_cur; //fifo指针,被指针指定的位置会在下一次写有效时写入数据
reg [$clog2(32)-1:0] ram_pointer_nxt; //代表当前位置是空的

assign dout = mem[0];



always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		ram_pointer_cur <= 1'b0; 
	else
		ram_pointer_cur <= ram_pointer_nxt; 
end

always@(*)begin
	case({wr_en,rd_en})
		2'b10:ram_pointer_nxt = ram_pointer_cur + 1;
		2'b01:ram_pointer_nxt = ram_pointer_cur - 1;
		default:ram_pointer_nxt = ram_pointer_cur; 
	endcase
end

always@(*)begin
	case({wr_en,rd_en})
		2'b11:begin w_ram_line = 32'd0; w_ram_line[ram_pointer_cur-1] = 1'd1; end 
		2'b10:begin w_ram_line = 32'd0; w_ram_line[ram_pointer_cur] = 1'd1; end 
		default:w_ram_line = 32'd0; 
	endcase
end



genvar i;
generate 
	for(i=0;i<31;i=i+1)begin
		always@(posedge clk or negedge rst_n)
		begin
			if(!rst_n)
				mem[i] <=1'b0;
			else begin
				if(w_ram_line[i])
					mem[i] <=din;
				else if(rd_en)//仅当ram被只读的时刻,ram整体上移;
					mem[i] <=mem[i+1];
			end
		end
	end
endgenerate

always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		mem[31] <=1'b0;
	else begin
		if(w_ram_line[31])
			mem[31] <=din;
		else if(rd_en)//仅当ram被只读的时刻,ram整体上移,这是最后一个格子,就补0;
			mem[31] <=0;
	end
end

always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		fifo_empty <= 1'b0; 
	else if(ram_pointer_nxt=='d0)
		fifo_empty <= 1'b1; 
	else
		fifo_empty <= 1'b0; 
end
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		fifo_full <= 1'b0; 
	else if(ram_pointer_nxt=='d31)
		fifo_full <= 1'b1; 
	else
		fifo_full <= 1'b0; 
end


//mem monitor
wire [7:0] mem_0;
wire [7:0] mem_1;
wire [7:0] mem_2;
wire [7:0] mem_3;
wire [7:0] mem_4;
wire [7:0] mem_5;
wire [7:0] mem_6;
wire [7:0] mem_7;
wire [7:0] mem_8;
wire [7:0] mem_9;
wire [7:0] mem_10;
wire [7:0] mem_11;
wire [7:0] mem_12;
wire [7:0] mem_13;
wire [7:0] mem_14;
wire [7:0] mem_15;
wire [7:0] mem_16;
wire [7:0] mem_17;
wire [7:0] mem_18;
wire [7:0] mem_19;
wire [7:0] mem_20;
wire [7:0] mem_21;
wire [7:0] mem_22;
wire [7:0] mem_23;
wire [7:0] mem_24;
wire [7:0] mem_25;
wire [7:0] mem_26;
wire [7:0] mem_27;
wire [7:0] mem_28;
wire [7:0] mem_29;
wire [7:0] mem_30;
wire [7:0] mem_31;
assign mem_0  = mem[0];
assign mem_1  = mem[1];
assign mem_2  = mem[2];
assign mem_3  = mem[3];
assign mem_4  = mem[4];
assign mem_5  = mem[5];
assign mem_6  = mem[6];
assign mem_7  = mem[7];
assign mem_8  = mem[8];
assign mem_9  = mem[9];
assign mem_10 = mem[10];
assign mem_11 = mem[11];
assign mem_12 = mem[12];
assign mem_13 = mem[13];
assign mem_14 = mem[14];
assign mem_15 = mem[15];
assign mem_16 = mem[16];
assign mem_17 = mem[17];
assign mem_18 = mem[18];
assign mem_19 = mem[19];
assign mem_20 = mem[20];
assign mem_21 = mem[21];
assign mem_22 = mem[22];
assign mem_23 = mem[23];
assign mem_24 = mem[24];
assign mem_25 = mem[25];
assign mem_26 = mem[26];
assign mem_27 = mem[27];
assign mem_28 = mem[28];
assign mem_29 = mem[29];
assign mem_30 = mem[30];
assign mem_31 = mem[31];
//-------------------



endmodule


        下面说一说设计思路,首先,激励发出写outstanding,握手成功后,会将指令信息存入AWFIFO中。这样AWFIFO的状态就为非空,表示着有未处理完成的outstanding。如果此时,WFIFO中的数据,已经达到了该次outstanding所需的数据量。而且此时写state未处于有效状态,那么就拉高写state,进而发生一次写ram操作。写ram的首地址由AWADDR控制,使用计数器生成偏移地址。计数器的最大值由AWLEN控制。读取WFIFO同时开启RAM写使能。联合计数器产生的地址,能够将从WFIFO中读出的数据成功写入至RAM。如此一次写RAM操作完成,将AWID压入B通道,抬高BVALID,预示一次AXI写操作完成。读操作与写操作的主要逻辑可以说完全相同,只是数据方向不同。下面列出rtl和部分注释,欢迎大家一起交流。下一节会有测试文件和波形分析。

module axi2dpram(
//都是常用的接口信号,复杂的信号未列出
	input aclk,
	input arst_n,

	//aw channel
	input [7:0]aw_id,
	input [31:0]aw_addr,
	input [3:0]aw_len,
	input [2:0] aw_size,
	input aw_valid,
	output aw_ready,

	//w channel
	input [31:0] w_data,
	input [3:0]  w_strb,
	input        w_last,
	input w_valid,
	output w_ready,

	//b channel
	output  [7:0] b_id,
	output  b_valid,
	input  b_ready,

	//ar channel
	input [7:0]ar_id,
	input [31:0]ar_addr,
	input [3:0]ar_len,
	input [2:0] ar_size,
	input ar_valid,
	output ar_ready,

	//r channel
	output  [7:0] r_id,
	output  [31:0] r_data,
	output        r_last,
	output        r_valid,
	input         r_ready
	
);

	wire ram_wr_en;
	wire ram_rd_en;
	wire [7:0] ram_wr_addr;
	wire [7:0] ram_rd_addr;
	wire [31:0] ram_wr_data;
	wire [31:0] ram_rd_data;

	wire aw_wren;
	wire aw_rden;
	wire [7+32+3-1:0] aw_din;
	wire [7+32+3-1:0] aw_dout;

	wire ar_wren;
	wire ar_rden;
	wire [7+32+3-1:0] ar_din;
	wire [7+32+3-1:0] ar_dout;

	wire w_wren;
	wire w_rden;
	wire [32+4-1:0] w_din;
	wire [32+4-1:0] w_dout;
	wire [4:0]	w_pointer;

	reg r_wren;
	wire r_rden;
	wire [32+1+8-1:0] r_din;
	wire [32+1+8-1:0] r_dout;

	wire [7:0]b_din ;
	wire [7:0]b_dout ;
	wire b_wren ; 
	wire b_rden ;


	wire [7:0]  id_mark;
	wire [31:0] addr_mark;
	wire [3:0]  len_mark;
	reg  [4:0]   w_counter;
	wire [4:0]  len_counter_max;
	reg w_state;
	reg w_state_start;

	wire [7:0]  id_mark_r;
	wire [31:0] addr_mark_r;
	wire [3:0]  len_mark_r;
	reg  [4:0]   r_counter;
	wire [4:0]  len_counter_max_r;
	reg r_state;
	reg r_state_start;
	reg r_last_prepare;
	reg [7:0] r_id_prepare;

	wire aw_sync,aw_full,aw_empty;
	wire ar_sync,ar_full,ar_empty,r_empty;
	wire w_sync,w_full;
	wire b_sync,b_empty;


	assign aw_ready = !aw_full;//输出到 manager
	assign w_ready  = !w_full;//输出到 manager
	assign b_valid  = !b_empty;//输出到 manager
	assign ar_ready = !ar_full;//输出到manager
	assign r_valid  = !r_empty;//输出到 manager

	assign aw_sync = aw_valid && aw_ready; //握手信号
	assign w_sync  = w_valid  && w_ready;//握手信号
	assign b_sync  = b_ready &&  b_valid ;//握手信号
	assign ar_sync = ar_valid && ar_ready;//握手信号
	assign r_sync  = r_ready && r_valid;//握手信号


	//-------------------------------------------------------------
	//-------------------write main logic---------------------------
	//-------------------------------------------------------------
	
	//aw fifo opreat 
	assign aw_din = {aw_id,aw_addr,aw_len}; //aw fifo data in
	assign aw_wren = aw_sync; 
	assign aw_rden = (w_counter == len_counter_max) ? 1:0; //当达到len最大的时候,开始读aw一次。

	//w fifo opreat 
	assign w_din = {w_data,w_strb};//w fifo data in
	assign w_wren = w_sync;
	assign w_rden = w_state && (w_counter!=1'b0); 

	//b fifo opreat 
	assign b_din =  id_mark;
	assign b_wren = (w_counter == len_counter_max) ? 1:0; 
	assign b_rden = b_sync;
	assign b_id   = b_dout;

	assign {id_mark,addr_mark,len_mark} = aw_dout;
	assign len_counter_max = len_mark + 1'b1 ;
//写开始信号产生,脉冲信号
	always@(posedge aclk or negedge arst_n)
	begin
		if(!arst_n)
	 		w_state_start <= 1'b0; 
		else begin
			if(!aw_empty && (w_pointer >= len_counter_max) && !w_state )
				w_state_start <= 1'b1;
			else
				w_state_start <= 1'b0;
		end
	end
//写信号产生,电平信号
	always@(posedge aclk or negedge arst_n)
	begin
		if(!arst_n)
			w_state <= 1'b0;
		else begin
			if(w_counter == len_counter_max)
				w_state <= 1'b0;
			else if(w_state_start)
				w_state <= 1'b1;
			else
				w_state <= w_state;
		end
	end
//计数每一个outstanding里面的transfer的数量,达到了len+1之后归零
	always@(posedge aclk or negedge arst_n)
	begin
		if(!arst_n)
			w_counter <= 5'b0;
		else begin
			if(w_counter == len_counter_max)
				w_counter <= 5'b0;
			else if(w_state)
				w_counter <= w_counter + 1'b1;
			else
				w_counter <= w_counter;
		end
	end


	//ram w
	assign ram_wr_en = w_rden;
	assign ram_wr_data = w_dout[32+4-1:4];
	assign ram_wr_addr = addr_mark[31:2] + (w_counter - 1'b1);


	//-------------------------------------------------------------
	//-------------------read main logic---------------------------
	//-------------------------------------------------------------


	//ar fifo opreat 
	assign {id_mark_r,addr_mark_r,len_mark_r} = ar_dout;
	assign ar_din = {ar_id,ar_addr,ar_len};
	assign ar_wren = ar_sync;
	assign ar_rden = (r_counter == len_counter_max_r) ? 1:0; 


	//r fifo opreat 
//r_last_prepare 
	always@(posedge aclk or negedge arst_n)
	begin
		if(!arst_n)
			r_last_prepare <= 1'b0;
		else
			r_last_prepare <= ((r_counter == len_counter_max_r) ? 1:0);
	end
//r_id_prepare
	always@(posedge aclk or negedge arst_n)
	begin
		if(!arst_n)
			r_id_prepare <= 1'b0;
		else
			r_id_prepare <= id_mark_r;
	end

	assign r_din = {r_id_prepare,ram_rd_data,r_last_prepare};

	always@(posedge aclk or negedge arst_n)// 延迟一个周期 
	begin
		if(!arst_n)
			r_wren <= 1'b0;
		else
			r_wren <= ram_rd_en;
	end

	assign r_rden = r_sync;

	assign {r_id,r_data,r_last} = r_dout;



	//----
	assign len_counter_max_r = len_mark_r + 1'b1 ;

	always@(posedge aclk or negedge arst_n)
	begin
		if(!arst_n)
	 		r_state_start <= 1'b0; 
		else begin
			if(!ar_empty && !r_state )
				r_state_start <= 1'b1;
			else
				r_state_start <= 1'b0;
		end
	end
	always@(posedge aclk or negedge arst_n)
	begin
		if(!arst_n)
			r_state <= 1'b0;
		else begin
			if(r_counter == len_counter_max_r)
				r_state <= 1'b0;
			else if(r_state_start)
				r_state <= 1'b1;
			else
				r_state <= r_state;
		end
	end
	always@(posedge aclk or negedge arst_n)
	begin
		if(!arst_n)
			r_counter <= 1'b0;
		else begin
			if(r_counter == len_counter_max_r)
				r_counter <= 1'b0;
			else if(r_state)
				r_counter <= r_counter + 1'b1;
			else
				r_counter <= r_counter;
		end
	end


	//ram r
	assign ram_rd_en = r_state && (r_counter!=1'b0);
	assign ram_rd_addr = addr_mark_r[31:2] + (r_counter - 1'b1);


//用于位宽补齐
wire [63:0]good0;
wire [63:0]good1;
wire [63:0]good2;
wire [63:0]good3;
wire [63:0]good4;
wire [63:0]good5;
wire [63:0]good6;
wire [63:0]good7;
wire [63:0]good8;
wire [63:0]good9;


assign good0={22'b0,aw_din};

assign aw_dout=good1[41:0];



	sync_fifo   awfifo(
		.rst_n(arst_n),
		.clk(aclk),

		.wr_en(aw_wren),
		.din(good0),

		.rd_en(aw_rden),
        	.dout(good1),	

		.fifo_empty(aw_empty),
		.fifo_full(aw_full)
	);



assign good2={28'b0,w_din};
assign w_dout=good3[35:0];


	sync_fifo   wfifo(
		.rst_n(arst_n),
		.clk(aclk),

		.wr_en(w_wren),
		.din(good2),

		.rd_en(w_rden),
        	.dout(good3),	

		.fifo_empty(),
		.fifo_full(w_full),
		.pointer(w_pointer)
	);

assign good4={56'b0,b_din};
assign b_dout=good5[7:0];



	sync_fifo   bfifo(
		.rst_n(arst_n),
		.clk(aclk),

		.wr_en(b_wren),
		.din(good4),

		.rd_en(b_rden),
        	.dout(good5),	

		.fifo_empty(b_empty),
		.fifo_full()
	);

assign good6={20'b0,ar_din};
assign ar_dout=good7[43:0];


	sync_fifo   arfifo(
		.rst_n(arst_n),
		.clk(aclk),

		.wr_en(ar_wren),
		.din(good6),

		.rd_en(ar_rden),
        	.dout(good7),	

		.fifo_empty(ar_empty),
		.fifo_full(ar_full)
	);

assign good8={23'b0,r_din};
assign r_dout=good9[40:0];





	sync_fifo   rfifo(
		.rst_n(arst_n),
		.clk(aclk),

		.wr_en(r_wren),
		.din(good8),

		.rd_en(r_rden),
        	.dout(good9),	
		.fifo_empty(r_empty),
		.fifo_full(r_full)
	);


	dpram#(.RAM_WIDTH(32),
	      .RAM_DEPTH(256),
	      .ADDR_LINE(8))
	func1(
		.wr_clk (aclk),
		.rd_clk (aclk),
		.wr_en  (ram_wr_en),
		.rd_en  (ram_rd_en),
		.wr_addr(ram_wr_addr),
		.rd_addr(ram_rd_addr),
		.wr_data(ram_wr_data),
		.rd_data(ram_rd_data)
	);

endmodule

  • 2
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值