/******************************************************************************* * Module: simul_saxi_gp_wr * Date:2015-08-04 * Author: Andrey Filippov * Description: Simplified model of AXI_GP write channel * * Copyright (c) 2015 Elphel, Inc. * simul_saxi_gp_wr.v is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * simul_saxi_gp_wr.v is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . *******************************************************************************/ `timescale 1ns/1ps module simul_saxi_gp_wr( input rst, // AXI signals input aclk, output aresetn, // do not use? // write address input [31:0] awaddr, input awvalid, output awready, input [ 5:0] awid, input [ 1:0] awlock, // verify the correct values are here input [ 3:0] awcache, // verify the correct values are here input [ 2:0] awprot, // verify the correct values are here input [ 3:0] awlen, input [ 1:0] awsize, input [ 1:0] awburst, input [ 3:0] awqos, // verify the correct values are here // write data input [31:0] wdata, input wvalid, output wready, input [ 5:0] wid, input wlast, input [ 3:0] wstrb, // write response output bvalid, input bready, output [ 5:0] bid, output [ 1:0] bresp, // Simulation signals - use same aclk output [31:0] sim_wr_address, output [ 5:0] sim_wid, output sim_wr_valid, // ready to provide simulation data input sim_wr_ready, // simulation may pause this channel by keeping this signal inactive output [31:0] sim_wr_data, output [ 3:0] sim_wr_stb, output [ 1:0] sim_wr_size, input [ 3:0] sim_bresp_latency, // latency in writing data outside of the module output [ 3:0] sim_wr_qos ); localparam AW_FIFO_DEPTH = 3; // FIFO number of address bits to fit AW_FIFO_NUM (number is one bit wider) localparam W_FIFO_DEPTH = 3; // FIFO number of address bits to fit W_FIFO_NUM localparam [AW_FIFO_DEPTH:0] AW_FIFO_NUM = 8; // Maximal number of words in AW FIFO 8-words localparam [W_FIFO_DEPTH:0] W_FIFO_NUM = 8; // Maximal number of words in AW 8-words localparam VALID_AWLOCK = 2'b0; // TODO localparam VALID_AWCACHE = 4'b0011; // localparam VALID_AWPROT = 3'b000; localparam VALID_AWLOCK_MASK = 2'b11; // TODO localparam VALID_AWCACHE_MASK = 4'b0011; // localparam VALID_AWPROT_MASK = 3'b010; /* http://forums.xilinx.com/t5/Embedded-Processor-System-Design/Accessing-DDR-from-PL-on-Zynq/m-p/324877#M8413 Solved it! To make it work, I set the (AR/AW)CACHE=0x11 and (AR/AW)PROT=0x00. In the CDMA datasheet, these were the recommended values, which I confirmed with ChipScope, when attached to CDMA's master port. The default values set by VHLS were 0x00 and 0x10 respectively, which is also the case in the last post. Alex */ wire aw_nempty; wire w_nempty; reg [11:0] next_wr_address_w; // bits that are incremented in 32-bit mode (higher are kept according to AXI 4KB inc. limit) reg [31:0] write_address; wire fifo_wd_rd; // read data fifo wire last_confirmed_write; wire [5:0] awid_out; // verify it matches wid_out when outputting data wire [1:0] awburst_out; wire [1:0] awsize_out; wire [3:0] awlen_out; wire [31:0] awaddr_out; wire [5:0] wid_out; wire wlast_out; wire [3:0] wstrb_out; wire [31:0] wdata_out; reg fifo_data_we_d; reg fifo_addr_we_d; reg [3:0] write_left; reg [ 1:0] wburst; // registered burst type reg [ 3:0] wlen; // registered awlen type (for wrapped over transfers) reg [ 1:0] wsize; wire start_write_burst_w; wire write_in_progress_w; // should go inactive last confirmed upstream cycle reg write_in_progress; reg [ 7:0] num_full_data = 0; // Number of full data bursts in FIFO wire [5:0] wresp_num_in_fifo; reg was_wresp_re=0; wire wresp_re; wire [AW_FIFO_DEPTH:0] wacount; wire [W_FIFO_DEPTH:0] wcount; // documentation sais : "When set, allows the priority of a transaction at the head of the WrCmdQ to be promoted if higher // priority transactions are backed up behind it." Whqt about demotion? Assuming it is not demoted assign aresetn= ~rst; // probably not needed at all - docs say "do not use" // generate ready signals for address and data // assign wready= !wcount[7] && (!(&wcount[6:0]) || !fifo_data_we_d); assign wready = (wcount < W_FIFO_NUM) && ((wcount < (W_FIFO_NUM-1)) || !fifo_data_we_d); always @ (posedge rst or posedge aclk) begin if (rst) fifo_data_we_d<=0; else fifo_data_we_d <= wready && wvalid; end // assign awready= !wacount[5] && (!(&wacount[4:0]) || !fifo_addr_we_d); assign awready = (wacount < AW_FIFO_NUM) && ((wacount < (AW_FIFO_NUM-1)) || !fifo_addr_we_d); always @ (posedge rst or posedge aclk) begin if (rst) fifo_addr_we_d<=0; else fifo_addr_we_d <= awready && awvalid; end // Count full data bursts ready in FIFO always @ (posedge rst or posedge aclk) begin if (rst) num_full_data <=0; else if (wvalid && wready && wlast && !start_write_burst_w) num_full_data <= num_full_data + 1; else if (!(wvalid && wready && wlast) && start_write_burst_w) num_full_data <= num_full_data - 1; end assign sim_wr_address= write_address; assign fifo_wd_rd= write_in_progress && w_nempty && sim_wr_ready; assign sim_wr_valid= write_in_progress && w_nempty; // for continuing writes assign last_confirmed_write = (write_left==0) && fifo_wd_rd && wlast_out; // wlast_out should take precedence over write_left? assign start_write_burst_w= aw_nempty && w_nempty && (! write_in_progress || last_confirmed_write); assign write_in_progress_w= (aw_nempty && w_nempty) || (write_in_progress && !last_confirmed_write); // AXI: Bursts should not cross 4KB boundaries (... and to limit size of the address incrementer) // in 64 bit mode - low 3 bits are preserved, next 9 are incremented always @* begin case (wburst) 2'h0: next_wr_address_w[11:0] <= write_address[11:0]; 2'h1: next_wr_address_w[11:0] <= write_address[11:0] + (1 << wsize); 2'h2: case (wsize) 2'h3: begin next_wr_address_w[11:3] <= (write_address[11:3] + 1) & {5'h1f, ~wlen[3:0]}; next_wr_address_w[ 2:0] <= write_address[2:0]; end 2'h2: begin next_wr_address_w[11:2] <= (write_address[11:2] + 1) & {6'h3f, ~wlen[3:0]}; next_wr_address_w[ 1:0] <= write_address[1:0]; end 2'h1: begin next_wr_address_w[11:1] <= (write_address[11:1] + 1) & {7'h7f, ~wlen[3:0]}; next_wr_address_w[0:0] <= write_address[0:0]; end 2'h0: begin next_wr_address_w[11:0] <= (write_address[11:0] + 1) & {8'hff, ~wlen[3:0]}; end endcase 2'h3: next_wr_address_w[11:0] <= 12'bx; endcase end wire [3:0] sim_wr_mask = (awsize_out == 0)? 4'h1 : ((awsize_out == 1)? 4'h3 : 4'hf); assign sim_wr_data= wdata_out; assign sim_wid= wid_out; assign sim_wr_stb=wstrb_out & sim_wr_mask; // limit by data size assign sim_wr_size = awsize_out; always @ (posedge aclk) begin if (start_write_burst_w) begin if (awid_out != wid_out) begin $display ("%m: at time %t ERROR: awid=%h, wid=%h",$time,awid_out,wid_out); $stop; end end if (awvalid && awready) begin if (((awlock ^ VALID_AWLOCK) & VALID_AWLOCK_MASK) != 0) begin $display ("%m: at time %t ERROR: awlock = %h, valid %h with mask %h",$time, awlock, VALID_AWLOCK, VALID_AWLOCK_MASK); $stop; end if (((awcache ^ VALID_AWCACHE) & VALID_AWCACHE_MASK) != 0) begin $display ("%m: at time %t ERROR: awcache = %h, valid %h with mask %h",$time, awcache, VALID_AWCACHE, VALID_AWCACHE_MASK); $stop; end if (((awprot ^ VALID_AWPROT) & VALID_AWPROT_MASK) != 0) begin $display ("%m: at time %t ERROR: awprot = %h, valid %h with mask %h",$time, awprot, VALID_AWPROT, VALID_AWPROT_MASK); $stop; end end end always @ (posedge aclk or posedge rst) begin if (rst) wburst[1:0] <= 0; else if (start_write_burst_w) wburst[1:0] <= awburst_out[1:0]; if (rst) wlen[3:0] <= 0; else if (start_write_burst_w) wlen[3:0] <= awlen_out[3:0]; if (rst) wsize[1:0] <= 0; else if (start_write_burst_w) wsize[1:0] <= awsize_out[1:0]; if (rst) write_in_progress <= 0; else write_in_progress <= write_in_progress_w; if (rst) write_left <= 0; else if (start_write_burst_w) write_left <= awlen_out[3:0]; // precedence over inc else if (fifo_wd_rd) write_left <= write_left-1; //SuppressThisWarning ISExst Result of 32-bit expression is truncated to fit in 4-bit target. if (rst) write_address <= 32'bx; else if (start_write_burst_w) write_address <= awaddr_out; // precedence over inc else if (fifo_wd_rd) write_address <= {write_address[31:12],next_wr_address_w[11:0]}; end // localparam AW_FIFO_NUM = 8; // Maximal number of words in AW FIFO 8-words // localparam W_FIFO_NUM = 8; // Maximal number of words in AW 8-words fifo_same_clock_fill #( .DATA_WIDTH(50),.DATA_DEPTH(AW_FIFO_DEPTH)) // read - 4, write - 32? waddr_i ( .rst (rst), .clk (aclk), .sync_rst (1'b0), .we (awvalid && awready), .re (start_write_burst_w), .data_in ({awid[5:0], awburst[1:0], awsize[1:0], awlen[3:0], awaddr[31:0], awqos[3:0]}), .data_out ({awid_out[5:0], awburst_out[1:0],awsize_out[1:0],awlen_out[3:0],awaddr_out[31:0], sim_wr_qos[3:0]}), .nempty (aw_nempty), .half_full (), //aw_half_full), .under (), //waddr_under), // output reg .over (), //waddr_over), // output reg .wcount (), //waddr_wcount), // output[3:0] reg .rcount (), //waddr_rcount), // output[3:0] reg .num_in_fifo(wacount) // output[3:0] ); fifo_same_clock_fill #( .DATA_WIDTH(43),.DATA_DEPTH(W_FIFO_DEPTH)) wdata_i ( .rst(rst), .clk(aclk), .sync_rst (1'b0), .we(wvalid && wready), .re(fifo_wd_rd), //start_write_burst_w), // wrong .data_in({wlast, wid[5:0], wstrb[3:0], wdata[31:0]}), .data_out({wlast_out,wid_out[5:0], wstrb_out[3:0], wdata_out[31:0]}), .nempty(w_nempty), .half_full(), //w_half_full), .under (), //wdata_under), // output reg .over (), //wdata_over), // output reg .wcount (), //wdata_wcount), // output[3:0] reg .rcount (), //wdata_rcount), // output[3:0] reg .num_in_fifo(wcount) // output[3:0] ); // **** Write response channel **** wire [ 1:0] bresp_value=2'b0; wire [ 1:0] bresp_in; wire fifo_wd_rd_dly; wire [5:0] bid_in; // input [ 3:0] sim_bresp_latency, // latency in writing data outside of the module dly_16 #( .WIDTH(1) ) bresp_dly_16_i ( .clk(aclk), // input .rst(rst), // input .dly(sim_bresp_latency[3:0]), // input[3:0] .din(last_confirmed_write), //fifo_wd_rd), // input[0:0] .dout(fifo_wd_rd_dly) // output[0:0] ); // first FIFO for bresp - latency outside of the module // wresp per burst, not per item ! fifo_same_clock_fill #( .DATA_WIDTH(8),.DATA_DEPTH(5)) wresp_ext_i ( .rst(rst), .clk(aclk), .sync_rst (1'b0), .we(last_confirmed_write), // fifo_wd_rd), .re(fifo_wd_rd_dly), // not allowing RE next cycle after bvalid .data_in({wid_out[5:0],bresp_value[1:0]}), .data_out({bid_in[5:0],bresp_in[1:0]}), .nempty(), .half_full(), //), .under (), //wresp_under), // output reg .over (), //wresp_over), // output reg .wcount (), //wresp_wcount), // output[3:0] reg .rcount (), //wresp_rcount), // output[3:0] reg .num_in_fifo() // wresp_num_in_fifo) // output[3:0] ); assign wresp_re=bready && bvalid; // && !was_wresp_re; always @ (posedge rst or posedge aclk) begin if (rst) was_wresp_re<=0; else was_wresp_re <= wresp_re; end assign bvalid=|wresp_num_in_fifo[5:1] || (!was_wresp_re && wresp_num_in_fifo[0]); // second wresp FIFO (does it exist in the actual module)? fifo_same_clock_fill #( .DATA_WIDTH(8),.DATA_DEPTH(5)) wresp_i ( .rst(rst), .clk(aclk), .sync_rst (1'b0), .we(fifo_wd_rd_dly), .re(wresp_re), // not allowing RE next cycle after bvalid .data_in({bid_in[5:0],bresp_in[1:0]}), .data_out({bid[5:0],bresp[1:0]}), .nempty(), //bvalid), .half_full(), //), .under (), //wresp_under), // output reg .over (), //wresp_over), // output reg .wcount (), //wresp_wcount), // output[3:0] reg .rcount (), //wresp_rcount), // output[3:0] reg .num_in_fifo(wresp_num_in_fifo) // wresp_num_in_fifo) // output[3:0] ); endmodule