From 5034058e2100d2fd1c869d71570ac7933308f326 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Mon, 19 May 2014 23:46:01 -0600 Subject: [PATCH] added modules to test ddrc phy over axi read/write --- axi/axibram_write.v | 5 +- ddrc_control.v | 69 ++++++++++++++ ddrc_status.v | 50 +++++++++++ ddrc_test01.v | 209 +++++++++++++++++++++++++++++++------------ phy/ddrc_sequencer.v | 5 +- 5 files changed, 276 insertions(+), 62 deletions(-) create mode 100644 ddrc_control.v create mode 100644 ddrc_status.v diff --git a/axi/axibram_write.v b/axi/axibram_write.v index 82f8cf1..b74c855 100644 --- a/axi/axibram_write.v +++ b/axi/axibram_write.v @@ -89,7 +89,7 @@ module axibram_write #( reg [ 1:0] wburst; // registered burst type reg [ 3:0] wlen; // registered awlen type (for wrapped over transfers) wire [ADDRESS_BITS-1:0] next_wr_address_w; // next transfer address; - wire bram_we_w; // write BRAM memory + wire bram_we_w; //,bram_we_nonmasked; // write BRAM memory non-masked - should be combined with wire start_write_burst_w; wire write_in_progress_w; reg dev_ready_r; // device, selected at start burst @@ -98,7 +98,8 @@ module axibram_write #( (wburst[0]? {ADDRESS_BITS{1'b0}}:((write_address[ADDRESS_BITS-1:0]+1) & {{(ADDRESS_BITS-4){1'b1}}, ~wlen[3:0]})): (wburst[0]? (write_address[ADDRESS_BITS-1:0]+1):(write_address[ADDRESS_BITS-1:0])); - assign bram_we_w= w_nempty && write_in_progress && dev_ready_r; + assign bram_we_w= w_nempty && write_in_progress && dev_ready_r; +// assign bram_we_nonmasked= w_nempty && write_in_progress; assign start_write_burst_w=aw_nempty && (!write_in_progress || (w_nempty && (write_left[3:0]==4'b0))); assign write_in_progress_w=aw_nempty || (write_in_progress && !(w_nempty && (write_left[3:0]==4'b0))); diff --git a/ddrc_control.v b/ddrc_control.v new file mode 100644 index 0000000..3db240d --- /dev/null +++ b/ddrc_control.v @@ -0,0 +1,69 @@ +/******************************************************************************* + * Module: ddrc_control + * Date:2014-05-19 + * Author: Andrey Filippov + * Description: Temporary module with DDRC control / command registers + * + * Copyright (c) 2014 Elphel, Inc. + * ddrc_control.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. + * + * ddrc_control.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 ddrc_control #( + parameter AXI_WR_ADDR_BITS= 12, + parameter SELECT_ADDR = 'h800, // address to select this module + parameter SELECT_ADDR_MASK = 'h800, // address mask to select this module + parameter BUSY_ADDR = 'hc00, // address to generate busy + parameter BUSY_ADDR_MASK = 'hc00 // address mask to generate busy +)( + input clk, + input mclk, + input rst, + input [AXI_WR_ADDR_BITS-1:0] pre_waddr, // AXI write address, before actual writes (to generate busy), valid@start_burst + input start_wburst, // burst start - should generate ~ready (should be AND-ed with !busy internally) + input [AXI_WR_ADDR_BITS-1:0] waddr, // write address, valid with wr_en + input wr_en, // write enable + input [31:0] wdata, // write data, valid with waddr and wr_en + output busy, // interface busy (combinatorial delay from start_wburst and pre_addr +// control signals +// control: sequencer run + output [10:0] run_addr, // Start address of the physical sequencer (MSB = 0 - "manual", 1 -"auto") + output [ 3:0] run_chn, // channel number to use for I/O buffers + output run_seq, // single mclk pulse to start sequencer +// input run_done; // output - will go through other channel - sequencer done (add busy?) +// control: delays and mmcm setup + output [ 7:0] dly_data, // 8-bit IDELAY/ODELAY (fine) and MMCM phase shift + output [ 6:0] dly_addr, // address to select delay register + output ld_delay, // write dly_data to dly_address, one mclk active pulse + output set, // transfer (activate) all delays simultaneosly, 1 mclk pulse +// control: additional signals + output cmda_tri, // tri-state all command and address lines to DDR chip + output inv_clk_div, // invert clk_div to ISERDES + output [ 7:0] dqs_pattern, // DQS pattern during write (normally 8'h55) + output [ 7:0] dqm_pattern, // DQM pattern (just for testing, should be 8'h0) +// control: buffers pages + output [ 1:0] port0_page, // port 0 buffer read page (to be controlled by arbiter later, set to 2'b0) + output [ 1:0] port0_int_page, // port 0 PHY-side write to buffer page (to be controlled by arbiter later, set to 2'b0) + output [ 1:0] port1_page, // port 1 buffer write page (to be controlled by arbiter later, set to 2'b0) + output [ 1:0] port1_int_page // port 1 PHY-side buffer read page (to be controlled by arbiter later, set to 2'b0) + +); + reg busy_r=0; + reg selected_r=0; + +// assign busy=busy_r && start_wburst?(((pre_addr ^ SELECT_ADDR) & SELECT_ADDR_MASK)==0): selected_r; + assign busy=busy_r && start_wburst?(((pre_waddr ^ BUSY_ADDR) & BUSY_ADDR_MASK)==0): selected_r; + +endmodule + diff --git a/ddrc_status.v b/ddrc_status.v new file mode 100644 index 0000000..a6d8fde --- /dev/null +++ b/ddrc_status.v @@ -0,0 +1,50 @@ +/******************************************************************************* + * Module: ddrc_status + * Date:2014-05-19 + * Author: Andrey Filippov + * Description: Read status/radback information from the DDR controller + * + * Copyright (c) 2014 Elphel, Inc. + * ddrc_status.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. + * + * ddrc_status.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 ddrc_status#( + parameter AXI_RD_ADDR_BITS= 12, + parameter SELECT_ADDR = 'h800, // address to select this module + parameter SELECT_ADDR_MASK = 'h800, // address mask to select this module + parameter BUSY_ADDR = 'hc00, // address to generate busy + parameter BUSY_ADDR_MASK = 'hc00 // address mask to generate busy +)( + input clk, + input mclk, + input rst, + input [AXI_RD_ADDR_BITS-1:0] pre_raddr, // AXI reade address, before actual reads (to generate busy), valid@start_burst + input start_rburst, // burst start - should generate ~ready (should be AND-ed with !busy internally) + input [AXI_RD_ADDR_BITS-1:0] raddr, // read address, valid with rd_en + input rd_en, // read enable + output [31:0] rdata, // read data, should valid with raddr and rd_en + output busy, // interface busy (combinatorial delay from start_wburst and pre_addr +// status/readback signals + input run_done, // sequencer done (add busy?) + input run_busy, // sequencer busy + + input locked, // MMCM and PLL locked + input ps_rdy, // MMCM phase shift control ready + input [ 7:0] ps_out // MMCM phase shift value (in 1/56 of the Fvco period) +); + + +endmodule + diff --git a/ddrc_test01.v b/ddrc_test01.v index c8154b4..44e9369 100644 --- a/ddrc_test01.v +++ b/ddrc_test01.v @@ -46,7 +46,16 @@ module ddrc_test01 #( parameter CMD_PAUSE_BITS= 6, parameter CMD_DONE_BIT= 6, parameter AXI_WR_ADDR_BITS = 12, - parameter AXI_RD_ADDR_BITS = 12 + parameter AXI_RD_ADDR_BITS = 12, + parameter SELECT_WR_ADDR = 'h800, // AXI write address to select this module + parameter SELECT_WR_ADDR_MASK = 'h800, // AXI write address mask to select this module + parameter BUSY_WR_ADDR = 'hc00, // AXI write address to generate busy + parameter BUSY_WR_ADDR_MASK = 'hc00, // AXI write address mask to generate busy + parameter SELECT_RD_ADDR = 'h800, // AXI read address to select this module + parameter SELECT_RD_ADDR_MASK = 'h800, // AXI read address mask to select this module + parameter BUSY_RD_ADDR = 'hc00, // AXI read address to generate busy + parameter BUSY_RD_ADDR_MASK = 'hc00 // AXI read address mask to generate busy + )( // DDR3 interface output SDCLK, // DDR3 clock differential output, positive @@ -68,6 +77,7 @@ module ddrc_test01 #( inout NDQSU // ~UDQS I/O pad // AXI write (ps -> pl) ); + localparam ADDRESS_NUMBER=15; // Source for reset and clock wire [3:0] fclk; // PL Clocks [3:0], output wire [3:0] frst; // PL Clocks [3:0], output @@ -109,7 +119,8 @@ module ddrc_test01 #( wire axiwr_dev_ready; // extrernal combinatorial ready signal, multiplexed from different sources according to pre_awaddr@start_burst wire axiwr_bram_wclk; wire [AXI_WR_ADDR_BITS-1:0] axiwr_bram_waddr; - wire axiwr_bram_wen; // external memory wreite enable, (internally combined with registered dev_ready + wire axiwr_bram_wen; // external memory write enable, (internally combined with registered dev_ready +// SuppressWarnings VEditor unused (yet?) wire [3:0] axiwr_bram_wstb; wire [31:0] axiwr_bram_wdata; @@ -138,6 +149,7 @@ module ddrc_test01 #( wire axird_start_burst; // start of read burst, valid pre_araddr, save externally to control ext. dev_ready multiplexer wire axird_dev_ready; // extrernal combinatorial ready signal, multiplexed from different sources according to pre_araddr@start_burst // External memory interface +// SuppressWarnings VEditor unused (yet?) - use mclk wire axird_bram_rclk; // .rclk(aclk), // clock for read port wire [AXI_RD_ADDR_BITS-1:0] axird_bram_raddr; // .raddr(read_in_progress?read_address[9:0]:10'h3ff), // read address wire axird_bram_ren; // .ren(bram_reg_re_w) , // read port enable @@ -214,6 +226,7 @@ wire [10:0] run_addr; // input[10:0] wire [ 3:0] run_chn; // input[3:0] wire run_seq; // input wire run_done; // output +wire run_busy; // TODO: add to ddrc_sequencer wire [ 7:0] dly_data; // input[7:0] wire [ 6:0] dly_addr; // input[6:0] wire ld_delay; // input @@ -223,9 +236,87 @@ wire locked; // output wire ps_rdy; // output wire [ 7:0] ps_out; // output[7:0] +wire en_port0_rd; +wire en_port0_regen; +wire en_port1_wr; + +wire [ 1:0] port0_page; // input[1:0] +wire [ 1:0] port0_int_page; // input[1:0] + +wire [ 1:0] port1_page; // input[1:0] +wire [ 1:0] port1_int_page;// input[1:0] + +// additional control signals +wire cmda_tri; // input +wire inv_clk_div; // input +wire [ 7:0] dqs_pattern; // input[7:0] 8'h55 +wire [ 7:0] dqm_pattern; // input[7:0] 8'h00 -assign en_cmd0_wr= axiwr_bram_wen && (axiwr_bram_waddr[11:10]==3); +assign en_cmd0_wr= axiwr_bram_wen && (axiwr_bram_waddr[11:10]==2'h1); +assign en_port0_rd= axird_bram_ren && (axird_bram_raddr[11:10]==2'h0); +assign en_port0_regen= axird_bram_regen && (axird_bram_raddr[11:10]==2'h0); +assign en_port1_wr= axiwr_bram_wen && (axiwr_bram_waddr[11:10]==2'h0); +wire axiwr_dev_busy; +assign axiwr_dev_ready = ~axiwr_dev_busy; //may combine (AND) multiple sources if needed + +wire axird_dev_busy; +assign axird_dev_ready = ~axird_dev_busy; //may combine (AND) multiple sources if needed + ddrc_control #( + .AXI_WR_ADDR_BITS (AXI_WR_ADDR_BITS), + .SELECT_ADDR (SELECT_WR_ADDR), + .SELECT_ADDR_MASK (SELECT_WR_ADDR_MASK), + .BUSY_ADDR (BUSY_WR_ADDR), + .BUSY_ADDR_MASK (BUSY_WR_ADDR_MASK) + ) ddrc_control_i ( + .clk (axi_aclk), // input + .mclk (axiwr_bram_wclk), // input + .rst (axi_rst), // input + .pre_waddr (axiwr_pre_awaddr[AXI_WR_ADDR_BITS-1:0]), // input[11:0] + .start_wburst (axiwr_start_burst), // input + .waddr (axiwr_bram_waddr[AXI_WR_ADDR_BITS-1:0]), // input[11:0] + .wr_en (axiwr_bram_wen), // input + .wdata (axiwr_bram_wdata[31:0]), // input[31:0] (no input for wstb here) + .busy (axiwr_dev_busy), // output + .run_addr (run_addr[10:0]), // output[10:0] + .run_chn (run_chn[3:0]), // output[3:0] + .run_seq (run_seq), // output + .dly_data (dly_data[7:0]), // output[7:0] + .dly_addr (dly_addr[6:0]), // output[6:0] + .ld_delay (ld_delay), // output + .set (set), // output + .cmda_tri (cmda_tri), // output + .inv_clk_div (inv_clk_div), // output + .dqs_pattern (dqs_pattern[7:0]), // output[7:0] + .dqm_pattern (dqm_pattern[7:0]), // output[7:0] + .port0_page (port0_page[1:0]), // output[1:0] + .port0_int_page (port0_int_page[1:0]), // output[1:0] + .port1_page (port1_page[1:0]), // output[1:0] + .port1_int_page (port1_int_page[1:0]) // output[1:0] + ); + + ddrc_status #( + .AXI_RD_ADDR_BITS (AXI_RD_ADDR_BITS), + .SELECT_ADDR (SELECT_RD_ADDR), + .SELECT_ADDR_MASK (SELECT_RD_ADDR_MASK), + .BUSY_ADDR (BUSY_RD_ADDR), + .BUSY_ADDR_MASK (BUSY_RD_ADDR_MASK) + ) ddrc_status_i ( + .clk (axi_aclk), // input + .mclk (mclk), // input + .rst (axi_rst), // input + .pre_raddr (axird_pre_araddr[AXI_RD_ADDR_BITS-1:0]), // input[11:0] + .start_rburst (axird_start_burst), // input + .raddr (axird_bram_raddr[AXI_RD_ADDR_BITS-1:0]), // input[11:0] + .rd_en (axird_bram_regen), // input + .rdata (axird_bram_rdata[31:0]), // output[31:0] + .busy (axird_dev_busy), // output + .run_done (run_done), // input + .run_busy (run_busy), // input + .locked (locked), // input + .ps_rdy (ps_rdy), // input + .ps_out (ps_out[7:0]) // input[7:0] + ); ddrc_sequencer #( @@ -254,63 +345,65 @@ assign en_cmd0_wr= axiwr_bram_wen && (axiwr_bram_waddr[11:10]==3); .CMD_PAUSE_BITS (CMD_PAUSE_BITS), .CMD_DONE_BIT (CMD_DONE_BIT) ) ddrc_sequencer_i ( - .SDCLK (SDCLK), // output - .SDNCLK (SDNCLK), // output - .SDA (SDA), // output[14:0] - .SDBA (SDBA), // output[2:0] - .SDWE (SDWE), // output - .SDRAS (SDRAS), // output - .SDCAS (SDCAS), // output - .SDCKE (SDCKE), // output - .SDODT (SDODT), // output - .SDD (SDD), // inout[15:0] - .SDDML (SDDML), // inout - .DQSL (DQSL), // inout - .NDQSL (NDQSL), // inout - .SDDMU (SDDMU), // inout - .DQSU (DQSU), // inout - .NDQSU (NDQSU), // inout - .clk_in (axi_aclk), // input - .rst_in (axi_rst), // input - .mclk (mclk), // output - .cmd0_clk (axi_aclk), // input - .cmd0_we (en_cmd0_wr), // input - .cmd0_addr (axiwr_bram_waddr[9:0]), // input[9:0] - .cmd0_data (axiwr_bram_wdata[31:0]), // input[31:0] - .cmd1_clk (mclk), // input - .cmd1_we (1'b0), // input - .cmd1_addr (10'b0), // input[9:0] - .cmd1_data (32'b0), // input[31:0] + .SDCLK (SDCLK), // output + .SDNCLK (SDNCLK), // output + .SDA (SDA[14:0]), // output[14:0] // BUG with localparam - fixed + .SDBA (SDBA[2:0]), // output[2:0] + .SDWE (SDWE), // output + .SDRAS (SDRAS), // output + .SDCAS (SDCAS), // output + .SDCKE (SDCKE), // output + .SDODT (SDODT), // output + .SDD (SDD[15:0]), // inout[15:0] + .SDDML (SDDML), // inout + .DQSL (DQSL), // inout + .NDQSL (NDQSL), // inout + .SDDMU (SDDMU), // inout + .DQSU (DQSU), // inout + .NDQSU (NDQSU), // inout + .clk_in (axi_aclk), // input + .rst_in (axi_rst), // input + .mclk (mclk), // output + .cmd0_clk (axi_aclk), // input + .cmd0_we (en_cmd0_wr), // input + .cmd0_addr (axiwr_bram_waddr[9:0]), // input[9:0] + .cmd0_data (axiwr_bram_wdata[31:0]), // input[31:0] + .cmd1_clk (mclk), // input + // TODO: add - from PL generation of the command sequences + .cmd1_we (1'b0), // input + .cmd1_addr (10'b0), // input[9:0] + .cmd1_data (32'b0), // input[31:0] - .run_addr (run_addr[10:0]), // input[10:0] - .run_chn (run_chn[3:0]), // input[3:0] - .run_seq (run_seq), // input - .run_done (run_done), // output - .dly_data (dly_data[7:0]), // input[7:0] - .dly_addr (dly_addr[6:0]), // input[6:0] - .ld_delay (ld_delay), // input - .set (set), // input - .locked (locked), // output - .ps_rdy (ps_rdy), // output - .ps_out (ps_out[7:0]), // output[7:0] + .run_addr (run_addr[10:0]), // input[10:0] + .run_chn (run_chn[3:0]), // input[3:0] + .run_seq (run_seq), // input + .run_done (run_done), // output + .run_busy (run_busy), // output + .dly_data (dly_data[7:0]), // input[7:0] + .dly_addr (dly_addr[6:0]), // input[6:0] + .ld_delay (ld_delay), // input + .set (set), // input + .locked (locked), // output + .ps_rdy (ps_rdy), // output + .ps_out (ps_out[7:0]), // output[7:0] - .port0_clk(), // input - .port0_re(), // input - .port0_regen(), // input - .port0_page(), // input[1:0] - .port0_int_page(), // input[1:0] - .port0_addr(), // input[7:0] - .port0_data(), // output[31:0] - .port1_clk(), // input - .port1_we(), // input - .port1_page(), // input[1:0] - .port1_int_page(), // input[1:0] - .port1_addr(), // input[7:0] - .port1_data(), // input[31:0] - .cmda_tri(), // input - .inv_clk_div(), // input - .dqs_pattern(), // input[7:0] - .dqm_pattern() // input[7:0] + .port0_clk (axi_aclk), // input + .port0_re (en_port0_rd), // input + .port0_regen (en_port0_regen), // input + .port0_page (port0_page[1:0]), // input[1:0] + .port0_int_page (port0_int_page[1:0]), // input[1:0] + .port0_addr (axird_bram_raddr[7:0]), // input[7:0] + .port0_data (axird_bram_rdata[31:0]), // output[31:0] + .port1_clk (axi_aclk), // input + .port1_we (en_port1_wr), // input + .port1_page (port1_page[1:0]), // input[1:0] + .port1_int_page (port1_int_page[1:0]), // input[1:0] + .port1_addr (axiwr_bram_waddr[7:0]), // input[7:0] + .port1_data (axiwr_bram_wdata[31:0]), // input[31:0] + .cmda_tri (cmda_tri), // input + .inv_clk_div (inv_clk_div), // input + .dqs_pattern (dqs_pattern), // input[7:0] + .dqm_pattern (dqm_pattern) // input[7:0] ); diff --git a/phy/ddrc_sequencer.v b/phy/ddrc_sequencer.v index f767808..ff5974d 100644 --- a/phy/ddrc_sequencer.v +++ b/phy/ddrc_sequencer.v @@ -82,7 +82,8 @@ module ddrc_sequencer #( input [10:0] run_addr, // controller sequencer start address (0..11'h3ff - cmd0, 11'h400..11'h7ff - cmd1) input [3:0] run_chn, // data channel to use input run_seq, // start controller sequence - output run_done, // controller sequence finished + output run_done, // controller sequence finished + output run_busy, // controller sequence in progress // inteface to control I/O delays and mmcm input [7:0] dly_data, // delay value (3 LSB - fine delay) input [6:0] dly_addr, // select which delay to program @@ -150,7 +151,7 @@ module ddrc_sequencer #( reg run_seq_d; assign run_done=sequence_done; - + assign run_busy=cmd_busy[0]; //earliest assign pause=cmd_fetch? (phy_cmd_nop && (pause_len != 0)): (cmd_busy[2] && (pause_cntr[CMD_PAUSE_BITS-1:1]!=0)); assign phy_cmd_word = phy_cmd_word?phy_cmd1_word:phy_cmd0_word;