上面学习了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