ddr控制-axi4映射到altera-uniPHY

发布时间 2023-03-23 22:52:08作者: 流计算

原始资料参考《emi_ddr_ug》,暂未下载到,故未粘贴datasheet原图。

  • local_size:burst 读写的最大数据数量。通常 IP 核内部有 FIFO 用于支持这样的连续数据读写,在Megafunction中设定好的最大数据数量是 Avl_size 的上限值。
  • local_be:byte enable 信号,用于使能或说是屏蔽读写数据的各个高低字节。
  • local_ready:总线当前状态指示。这里高电平表示 ready,此时的 local_read_req 和 local_write_req 能够被锁存。
  • local_burstbegin:突发传输起始标志位。它不受 local_ready 的影响,在发起一次读或写操作的第一个时钟周期,只需保持一个时钟周期的 local_burstbegin 为高电平状态,并且不用管此时的local_ ready状态如何。
  • local_addr:读写共用的总线地址,位宽由 DDR2 的存储总量和总线上读写数据的位宽来决定。如1Gbit的DDR2,外部芯片的数据位宽为 16bit,Avalon-MM 读写的数据位宽 64bit,那么它的地址不是以 16bit 位宽来计算的,而是以 64bit 位宽来计算的,即16M(24位)。
  • local_read_req:读请求, 配合地址local_addr 和突发传输起始标志位 local_burstbegin 发起一次 burst 读操作。在 local_burstbegin 拉高后,只需要确保在同一个时钟周期或其后第一次 local_ready 有效的时钟周期拉高一次 local_read_req 信号即可。
  • local_rdata_valid:读出数据的有效标志位。IP 核在收到 burst 读请求(local_read_req) 后的若千个时钟周期开始连续送出数据(数据可能分多次连续送出),该信号和读出数据配合,高电平表示当前读出数据有效。
  • local_rdata:读出数据。和local_rdata_valid 配合送给用户逻辑。
  • local_write_req:写请求信号。若发起一次 n 个数据写入的 burst 传输,第一个传输时钟周期首先拉高 local_burstbegin 以及 local_write_req,且local_write_req 必须保持到 n 个数据写入完成。只有在 local_ready 有效时,当前的 local_write_req、local_addr 和 local_wdata 才是有效的。
  • local_wdata:写入数据。

读写时序

写时序(by锆石科技,4突发,full速率)

img

从上图可以看出,除了 phy_clk 时钟信号外,写时序涉及到了 7 个信号,分别是 local_ready、local_burstbegin、local_write_req、local_size、local_address、local_be 和 local_wdata。

在这 7 个信号中只有 local_ready 是输出信号,这意味着我们需要通过 local_ready 来得知控制器是否准备好了接收我们的指令。

如果 local_ready 为高,则拉高 local_burstbegin 和 local_write_req 可以向控制器发出一次突发写指令,由于一次突发指令可能不止传输一个数据,因此 local_burstbegin 只需在突发开始时拉高一个时钟周期,而 local_write_req 在整个写数据期间都需拉高。在一次突发开始时需要指定突发的起始地址 local_address、突发大小 local_size,而在整个突发写期间,将每个数据以及它对应的字节使能信号顺序放在local_wdata 和 local_be 总线上。

如果 local_ready 为低,则表示控制器不能接收指令,在此期间除了 local_burstbegin 其他的信号都必须保持原来的状态,直至 local_ready 为高。

读时序(by锆石科技,4突发,full速率)

img

从上图可以看出,读时序和写时序类似,也有 8 个信号,除了 phy_clk 时钟信号,其他的信号有 local_ready、local_burstbegin、local_read_req 、local_size 、local_address、local_rdata_valid 和 local_rdata。

如果 local_ready 为高,则拉高local_burstbegin 和 local_read_req 一个时钟周期可以向控制器发出一次读突发指令,与此同时,必须给定突发起始地址 local_address 和突发大小 local_size。由于不需要像写时序那样逐个把数据放在 local_wdata 上,因此通常我们会连续发出读指令,当然前提是 local_ready 一直为高。

如果 local_ready 为低,则除了 local_ burstbegin 其他的信号都必须保持原状态,直到 local_ready 变为高。发出读指令一段时间后,local_rdata_valid 才会被拉高,表示 local_rdata 总线上的数据被读出。

最后需要说明的是,实际测试发现,local_ burstbegin 信号和控制器内部逻辑没有任何关联,也就是说不管 local_burstbegin 信号是什么状态都不影响读写数据过程。为此可以这么理解,local_ burstbegin信号的存在仅仅是为了兼容Avalon-MM总线,可实际上它却没有起到作用。

正常 4 个数据的 burst 写操作(by特权同学,8突发,half速率)

  默认情况下,local_addr 设定的是写入的首个数据对应的地址,随后每次写入数据后地址自动递增。

img

遇到 local_ready 拉低的 burst 写操作(by特权同学,8突发,half速率)

  Local_ready 拉高时,local_write_req、local_addr 和 local_wdata 所对应的地址和数据才是有效的。

img

正常 4 个数据的 burst 读操作(by特权同学,8突发,half速率)

  默认情况下,local_addr 为读出的首个数据对应的地址,随后将读出递增地址的数据。

img

遇到 local_ready 拉低的 burst 读操作(by特权同学,8突发,half速率)

  必须保持 local_read_req、local_size 和 local_addr 到 local_ready 拉高为止。

img


//////////////////////////////////////////////
// axi4 defines
localparam RESP = 2'b00;
localparam ID = 4'b0000;
// addr read
wire [1:0]C1_S1_AXI_arburst;
wire [2:0]C1_S1_AXI_arsize; 
wire [3:0]C1_S1_AXI_arid;
wire [3:0]C1_S1_AXI_arcache;
wire C1_S1_AXI_arlock;
wire [2:0]C1_S1_AXI_arprot;
wire [3:0]C1_S1_AXI_arqos;
assign C1_S1_AXI_arburst = 2'b01;//
assign C1_S1_AXI_arsize = 3'b111;// or 3'b001;
assign C1_S1_AXI_arid = ID;//
assign C1_S1_AXI_arcache = 4'b0011;
assign C1_S1_AXI_arlock = 1'b0;//unused
assign C1_S1_AXI_arprot = 3'b000;
assign C1_S1_AXI_arqos = 4'b0000;//unused
// addr write
wire [1:0]C1_S1_AXI_awburst;
wire [2:0]C1_S1_AXI_awsize;
wire [3:0]C1_S1_AXI_awid;
wire [3:0]C1_S1_AXI_awcache;
wire C1_S1_AXI_awlock;
wire [2:0]C1_S1_AXI_awprot;
wire [3:0]C1_S1_AXI_awqos;
assign C1_S1_AXI_awburst = 2'b01;//
assign C1_S1_AXI_awsize = 3'b111;// or 3'b001;
assign C1_S1_AXI_awid = ID;//
assign C1_S1_AXI_awcache = 4'b0011;
assign C1_S1_AXI_awlock = 1'b0;//unused
assign C1_S1_AXI_awprot = 3'b000;
assign C1_S1_AXI_awqos = 4'b0000;//unused
// respond
wire [1:0]C1_S1_AXI_bresp;
wire [3:0]C1_S1_AXI_bid;
assign C1_S1_AXI_bresp = RESP;// ?
assign C1_S1_AXI_bid = ID;//
// data
wire [1:0]C1_S1_AXI_rresp;
wire [3:0]C1_S1_AXI_rid;
assign C1_S1_AXI_rresp = RESP;// ?
assign C1_S1_AXI_rid = ID;//
wire [15:0]C1_S1_AXI_wstrb;
assign C1_S1_AXI_wstrb = 16'hFFFF;//
//////////////////////////////////////////////
// addr
wire C1_S1_AXI_arready;
wire C1_S1_AXI_arvalid;
wire [28:0]C1_S1_AXI_araddr;
wire [7:0]C1_S1_AXI_arlen;
wire C1_S1_AXI_awready;
wire C1_S1_AXI_awvalid;
wire [28:0]C1_S1_AXI_awaddr;
wire [7:0]C1_S1_AXI_awlen;
// respond
wire C1_S1_AXI_bready;//
wire C1_S1_AXI_bvalid;
// data
wire C1_S1_AXI_rready; // input
wire C1_S1_AXI_rvalid;
wire C1_S1_AXI_rlast;
wire [127:0]C1_S1_AXI_rdata;
wire C1_S1_AXI_wready;
wire C1_S1_AXI_wvalid;
wire C1_S1_AXI_wlast;
wire [127:0]C1_S1_AXI_wdata;

wire addr_ready;
wire addr_valid;
wire [28:0]local_address;
wire [7:0]addr_len;
assign addr_ready = C1_S1_AXI_arready&C1_S1_AXI_awready;

assign C1_S1_AXI_arvalid = addr_valid;
assign C1_S1_AXI_awvalid = addr_valid;
assign C1_S1_AXI_araddr = local_address;
assign C1_S1_AXI_awaddr = local_address;
assign C1_S1_AXI_arlen = addr_len;
assign C1_S1_AXI_awlen = addr_len;

wire data_ready;
wire data_valid;
wire data_last;
wire [127:0]data_data;
assign addr_ready = C1_S1_AXI_rready&C1_S1_AXI_wready;
assign C1_S1_AXI_rvalid = data_valid;
assign C1_S1_AXI_wvalid = data_valid;
assign C1_S1_AXI_rlast = local_address;
assign C1_S1_AXI_wlast = local_address;
assign C1_S1_AXI_rdata = data_data;
assign C1_S1_AXI_wdata = data_data;

axi4写操作

先写写地址,C1_S1_AXI_awaddr/C1_S1_AXI_awvalid等待C1_S1_AXI_awready有效即完成

再写数据,C1_S1_AXI_wdata/C1_S1_AXI_wvalid等待C1_S1_AXI_wready成功写入1个,最后一个C1_S1_AXI_wlast拉高1周期,即结束

疑问:突发长度C1_S1_AXI_awlen与写地址C1_S1_AXI_awaddr同时给到。


//local_burstbegin unused
//Add a write address stage
// write-stage1
assign local_ready = C1_S1_AXI_awready;
assign C1_S1_AXI_awvalid = local_write_req;
assign C1_S1_AXI_awlen = local_size;
assign C1_S1_AXI_awaddr = local_address;
// write-stage2
assign local_ready = C1_S1_AXI_wready;
assign local_write_req = C1_S1_AXI_wvalid;
assign local_wdata = C1_S1_AXI_wdata;

axi4读操作

先写读地址,C1_S1_AXI_araddr/C1_S1_AXI_arvalid等待C1_S1_AXI_arready有效即完成

再等待读出数据,在C1_S1_AXI_rready给高时C1_S1_AXI_rvalid有效会读出1个数据C1_S1_AXI_rdata,最后一个C1_S1_AXI_rlast拉高1周期,即结束

疑问:突发长度C1_S1_AXI_arlen与写地址C1_S1_AXI_araddr同时给到。


//local_burstbegin unused
// read
assign local_ready = C1_S1_AXI_arready;
assign C1_S1_AXI_arvalid = local_read_req;
assign C1_S1_AXI_arlen = local_size;
assign C1_S1_AXI_araddr = local_address;
assign C1_S1_AXI_rready = 1'b1;
assign local_rdata_valid = C1_S1_AXI_rvalid;
assign local_rdata = C1_S1_AXI_rdata;
//C1_S1_AXI_rlast


DRIVE_DDR.v

// local_
local_init_done,
local_ready,
local_address,
local_write_req,local_read_req,
local_size,
avl_burstbegin,


// input:local_init_done 替换为 init_calib_complete
// input:local_ready 改为 s_axi_arready,s_axi_awready/s_axi_wready 分[读写1/2]阶段选择使用。
				// 
// ouput:local_address 改为 s_axi_araddr,s_axi_awaddr 分[读写]阶段选择赋值
// ouput:local_size 改为 s_axi_arlen,s_axi_awlen 分[读写]阶段选择赋值
// ouput:avl_burstbegin 不需要了
// ouput:local_read_req 替换为 s_axi_arvalid
// ouput:local_write_req 改为 s_axi_awvalid,s_axi_wvalid 分[写1/2]阶段选择赋值

CTRL_DDR.v


avl_ready		,// 输入。在DRIVE_DDR.v和CTRL_DDR.v中使用。
					// 有3个ready信号 s_axi_arready,s_axi_awready/s_axi_wready
					// s_axi_arready:仅DRIVE_DDR.v用到
					// s_axi_awready:仅DRIVE_DDR.v用到
					// s_axi_wready:DRIVE_DDR.v和CTRL_DDR.v均用到。其中,在CTRL_DDR.v用作 fifo 接口写请求/使能
avl_burstbegin	,// 输出。DRIVE_DDR.v产生。在CTRL_DDR.v中使用。
					// 无需输出
avl_addr		,// 输出。DRIVE_DDR.v产生。
					// 在CTRL_DDR.v中输出2个信号 s_axi_araddr,s_axi_awaddr
avl_rdata_valid	,// 输入。在CTRL_DDR.v中使用。
					assign avl_rdata_valid = s_axi_rvalid; //替换
avl_rdata		,// 输入。在CTRL_DDR.v中使用。
					assign avl_rdata = s_axi_rdata; //替换
avl_wdata		,// 输出。在CTRL_DDR.v中产生。
					assign s_axi_wdata = avl_wdata; //替换
avl_be			,
avl_read_req	,// 输出。DRIVE_DDR.v产生。在CTRL_DDR.v中使用。
					assign s_axi_arvalid = avl_read_req; //替换
avl_write_req	,// 输出。DRIVE_DDR.v产生。在CTRL_DDR.v中使用。
					// 在CTRL_DDR.v中输出2个信号 s_axi_awvalid,s_axi_wvalid
					// 其中,用 s_axi_wvalid 作为 fifo 接口写请求/使能
avl_size		,// 输出。DRIVE_DDR.v产生。
					// 在CTRL_DDR.v中输出2个信号 s_axi_arlen,s_axi_awlen