/*! * Module:phy_cmd * @file phy_cmd.v * @date 2014-05-15 * @author Andrey Filippov * * @brief Executes a stream of commands to DDR3 phy at 1/2 ddr3 clock, global * (also proveides r/w interface to the x64 external buffer) * * @copyright Copyright (c) 2014 Elphel, Inc. * * License: * * phy_cmd.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. * * phy_cmd.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 . * * Additional permission under GNU GPL version 3 section 7: * If you modify this Program, or any covered work, by linking or combining it * with independent modules provided by the FPGA vendor only (this permission * does not extend to any 3-rd party modules, "soft cores" or macros) under * different license terms solely for the purpose of generating binary "bitstream" * files and/or simulating the code, the copyright holders of this Program give * you the right to distribute the covered work without those independent modules * as long as the source code for them is available from the FPGA vendor free of * charge, and there is no dependence on any encrypted modules for simulating of * the combined code. This permission applies to you if the distributed code * contains all the components and scripts required to completely simulate it * with at least one of the Free Software programs. */ `timescale 1ns/1ps module phy_cmd#( parameter ADDRESS_NUMBER = 15, parameter PHASE_WIDTH = 8, parameter SLEW_DQ = "SLOW", parameter SLEW_DQS = "SLOW", parameter SLEW_CMDA = "SLOW", parameter SLEW_CLK = "SLOW", parameter IBUF_LOW_PWR = "TRUE", parameter real REFCLK_FREQUENCY = 300.0, parameter HIGH_PERFORMANCE_MODE = "FALSE", parameter CLKIN_PERIOD = 10, //ns >1.25, 6001 and 1->0) wire phy_dqs_tri_in; // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) wire phy_dci_en_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) wire phy_dci_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) wire phy_dqs_toggle_en; //enable toggle DQS according to the pattern wire phy_buf_wr; // connect to extrenal buffer wire phy_buf_rd; // connect to extrenal buffer wire phy_buf_rst; // reset buffers to page start wire cmda_tri; wire [2:0] phy_rcw_cur; // {ras,cas,we} wire phy_odt_cur; // wire phy_cke_dis_cur; // disable cke (0 - enable), also controlled by a command bit ddr_cke (XOR-ed) wire phy_sel_cur; // first/second half-cycle, oter will be nop (cke+odt applicable to both) wire phy_dq_en_cur; //phy_dq_tri_in, // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) wire phy_dqs_en_cur; //phy_dqs_tri_in, // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) wire phy_dqs_toggle_cur;//enable toggle DQS according to the pattern wire phy_dci_en_cur; //phy_dci_in, // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) wire phy_buf_wr_cur; // connect to external buffer (but only if not paused) wire phy_buf_rd_cur; // connect to external buffer (but only if not paused) wire phy_buf_rst_cur; wire clk_div; reg [7:0] dly_data_r; // delay value (3 LSB - fine delay) reg [6:0] dly_addr_r; // select which delay to program reg ld_delay_r; // load delay data to selected iodelayl (clk_div synchronous) reg set_r; // clk_div synchronous set all delays from previously loaded values wire [2*ADDRESS_NUMBER-1:0] phy_addr; // also provides pause length when the command is NOP wire [ 5:0] phy_bank; wire [ 5:0] phy_rcw; // {ras,cas,we} wire [1:0] phy_odt; // may be optimized? wire [1:0] phy_cke; // may be optphy_dqs_tri_inimized? wire [7:0] phy_dq_tri; // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) wire [7:0] phy_dqs_tri; // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) wire phy_dci_dis_dq; wire phy_dci_dis_dqs; reg dqs_tri_prev, dq_tri_prev; wire phy_ps_rdy; wire [PHASE_WIDTH-1:0] phy_ps_out; reg ps_rdy_r1,ps_rdy_r2; reg locked_mmcm_r1,locked_mmcm_r2; reg locked_pll_r1, locked_pll_r2; reg dly_ready_r1, dly_ready_r2; reg dci_ready_r1, dci_ready_r2; reg [PHASE_WIDTH-1:0] ps_out_r1,ps_out_r2; wire [63:0] phy_rdata; // data read from ddr3 iserdese2 at posedge clk_div reg [63:0] phy_rdata_r; // registered @ posedge mclk reg [ADDRESS_NUMBER-1:0] phy_addr_prev; reg [ 2:0] phy_bank_prev; wire [ADDRESS_NUMBER-1:0] phy_addr_calm; wire [ 2:0] phy_bank_calm; reg [ 9:0] extra_prev; assign { phy_addr_in, phy_bank_in, phy_rcw_pos, // {ras,cas,we} phy_odt_in, // phy_cke_dis, // disable cke (0 - enable), also controlled by a command bit ddr_cke (XOR-ed) phy_sel_in, // fitst/second half-cycle, oter will be nop (cke+odt applicable to both) phy_dq_en_in, //phy_dq_tri_in, // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_en_in, //phy_dqs_tri_in, // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_toggle_en, //enable toggle DQS according to the pattern phy_dci_en_in, //phy_dci_in, // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) phy_buf_wr, // connect to external buffer (but only if not paused) phy_buf_rd, // connect to external buffer (but only if not paused) phy_cmd_add_pause, // add nop to current command phy_buf_rst // phy_spare // Reserved for future use } = phy_cmd_word; assign { phy_rcw_cur[2:0], // all set to 0 phy_odt_cur, // 9 8 ODT phy_cke_dis_cur, // 8 7 disable cke (0 - enable), also controlled by a command bit ddr_cke (XOR-ed) phy_sel_cur, // 7 6 first/second half-cycle, other will be nop (cke+odt applicable to both) - NOT USED? phy_dq_en_cur, // 6 5 phy_dq_tri_in, // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_en_cur, // 5 4 phy_dqs_tri_in, // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_toggle_cur,// 4 3 enable toggle DQS according to the pattern phy_dci_en_cur, // 3 2 phy_dci_in, // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) phy_buf_wr_cur, // 2 1 connect to external buffer (but only if not paused) phy_buf_rd_cur, // 1 0 connect to external buffer (but only if not paused) phy_buf_rst_cur // 0 } = add_pause ? {3'b0, extra_prev} : // 3'b0 for rcw (nop) { phy_rcw_pos[2:0], // {ras,cas,we} phy_odt_in, // may be optimized? phy_cke_dis, // disable cke (0 - enable), also controlled by a command bit ddr_cke (XOR-ed) phy_sel_in, // first/second half-cycle, oter will be nop (cke+odt applicable to both) phy_dq_en_in, //phy_dq_tri_in, // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_en_in, //phy_dqs_tri_in, // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_toggle_en, //enable toggle DQS according to the pattern phy_dci_en_in, //phy_dci_in, // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) phy_buf_wr, // connect to external buffer (but only if not paused) phy_buf_rd, // connect to external buffer (but only if not paused) phy_buf_rst }; assign phy_cke_in= phy_cke_dis_cur ^ ddr_cke; assign phy_dq_tri_in= ~phy_dq_en_cur; assign phy_dqs_tri_in=~phy_dqs_en_cur; assign phy_dci_in= ~phy_dci_en_cur; assign phy_rcw_in= ~phy_rcw_cur; assign phy_cmd_nop= (phy_rcw_pos==0) && !add_pause; // ignores inserted NOP assign sequence_done= phy_cmd_nop && phy_addr_in[CMD_DONE_BIT]; assign pause_len= phy_addr_in[CMD_DONE_BIT]? 0: phy_addr_in[CMD_PAUSE_BITS-1:0]; // protect from non-zero length with done bit assign phy_addr_calm= (phy_cmd_nop || add_pause) ? phy_addr_prev : phy_addr_in; assign phy_bank_calm= (phy_cmd_nop || add_pause) ? phy_bank_prev : phy_bank_in; assign buf_wr = phy_buf_wr_cur; assign buf_rd = phy_buf_rd_cur; assign buf_rst= phy_buf_rst_cur; assign phy_addr= {phy_addr_calm,phy_addr_calm}; // also provides pause length when the command is NOP assign phy_bank= {phy_bank_calm,phy_bank_calm}; assign phy_rcw= {phy_sel_cur?phy_rcw_in:3'h7, phy_sel_cur?3'h7:phy_rcw_in}; // {ras,cas,we} assign phy_odt= {phy_odt_cur,phy_odt_cur}; assign phy_cke= {phy_cke_in,phy_cke_in}; // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) assign phy_dq_tri= (dq_tri_prev==phy_dq_tri_in)?{{8{phy_dq_tri_in}}}: (dq_tri_prev?{dq_tri_on_pattern,dq_tri_on_pattern}:{dq_tri_off_pattern,dq_tri_off_pattern}); // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) assign phy_dqs_tri= (dqs_tri_prev==phy_dqs_tri_in)?{{8{phy_dqs_tri_in}}}: (dqs_tri_prev?{dqs_tri_on_pattern,dqs_tri_on_pattern}:{dqs_tri_off_pattern,dqs_tri_off_pattern}); assign phy_dci_dis_dq = phy_dci_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) assign phy_dci_dis_dqs = phy_dci_in || phy_odt_cur; // In write leveling mode phy_dci_in = 0, phy_odt_cur=1 - use DCI on DQ only, no DQS assign ps_rdy = ps_rdy_r2; assign ps_out = ps_out_r2; assign locked_mmcm = locked_mmcm_r2; assign locked_pll = locked_pll_r2; assign dly_ready = dly_ready_r2; assign dci_ready = dci_ready_r2; assign buf_wdata[63:0] = phy_rdata_r[63:0]; assign cmda_tri=!cmda_en; always @ (posedge mclk) begin dqs_tri_prev <= phy_dqs_tri_in; dq_tri_prev <= phy_dq_tri_in; end always @ (posedge mclk) begin if (mrst) begin phy_addr_prev <= 0; phy_bank_prev <= 0; extra_prev <= 0; end else if (!phy_cmd_nop) begin phy_addr_prev <= phy_addr_in; phy_bank_prev <= phy_bank_in; extra_prev <= { phy_odt_in, // 9 8 may be optimized? phy_cke_dis, // 8 7 disable cke (0 - enable), also controlled by a command bit ddr_cke (XOR-ed) phy_sel_in, // 7 6 first/second half-cycle, other will be nop (cke+odt applicable to both) phy_dq_en_in, // 6 5 phy_dq_tri_in, // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_en_in, // 5 4 phy_dqs_tri_in, // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_toggle_en,// 4 3 enable toggle DQS according to the pattern phy_dci_en_in, // 3 2 phy_dci_in, // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) phy_buf_wr, // 2 1 connect to external buffer (but only if not paused) phy_buf_rd, // 1 0 connect to external buffer (but only if not paused) phy_buf_rst // 0 connect to external buffer (but only if not paused) }; end end // cross clock boundary posedge mclk -> posedge clk_div (mclk is later than clk_div) reg rst_clk_div = 1; always @ (posedge clk_div) rst_clk_div <= mrst; always @ (posedge clk_div) begin if (rst_clk_div) begin dly_data_r <= 0; dly_addr_r <= 0; ld_delay_r <= 0; set_r <= 0; end else begin dly_data_r <= dly_data; dly_addr_r <= dly_addr; ld_delay_r <= ld_delay; set_r <= set; end end // cross clock boundary posedge posedge clk_div->negedge clk_div -> posedge mclk (mclk is later than clk_div) always @ (negedge clk_div) begin ps_rdy_r1 <= phy_ps_rdy; ps_out_r1 <= phy_ps_out; locked_mmcm_r1 <= phy_locked_mmcm; locked_pll_r1 <= phy_locked_pll; dly_ready_r1 <= phy_dly_ready; dci_ready_r1 <= phy_dci_ready; end always @ (posedge mclk) begin ps_rdy_r2 <= ps_rdy_r1; ps_out_r2 <= ps_out_r1; locked_mmcm_r2 <= locked_mmcm_r1; locked_pll_r2 <= locked_pll_r1; dly_ready_r2 <= dly_ready_r1; dci_ready_r2 <= dci_ready_r1; end always @ (negedge mclk) begin phy_rdata_r[63:0] <= phy_rdata[63:0]; end wire [7:0] dqs_data; assign dqs_data=phy_dqs_toggle_cur?dqs_pattern[7:0]:8'h0; // Has to be low to satisfy write levelling preamble phy_top #( .IOSTANDARD_DQ ("SSTL15_T_DCI"), .IOSTANDARD_DM ("SSTL15"), .IOSTANDARD_DQS ("DIFF_SSTL15_T_DCI"), .IOSTANDARD_CMDA ("SSTL15"), .IOSTANDARD_CLK ("DIFF_SSTL15"), .SLEW_DQ (SLEW_DQ), .SLEW_DQS (SLEW_DQS), .SLEW_CMDA (SLEW_CMDA), .SLEW_CLK (SLEW_CLK), .IBUF_LOW_PWR (IBUF_LOW_PWR), .IODELAY_GRP ("IODELAY_MEMORY"), .REFCLK_FREQUENCY (REFCLK_FREQUENCY), .HIGH_PERFORMANCE_MODE(HIGH_PERFORMANCE_MODE), .ADDRESS_NUMBER (ADDRESS_NUMBER), .PHASE_WIDTH (8), .BANDWIDTH ("OPTIMIZED"), .CLKIN_PERIOD (CLKIN_PERIOD), .CLKFBOUT_MULT (CLKFBOUT_MULT), .DIVCLK_DIVIDE (DIVCLK_DIVIDE), .CLKFBOUT_USE_FINE_PS (CLKFBOUT_USE_FINE_PS), .CLKFBOUT_PHASE (CLKFBOUT_PHASE), .SDCLK_PHASE (SDCLK_PHASE), .CLK_PHASE (CLK_PHASE), .CLK_DIV_PHASE (CLK_DIV_PHASE), .MCLK_PHASE (MCLK_PHASE), .REF_JITTER1 (REF_JITTER1), .SS_EN (SS_EN), .SS_MODE (SS_MODE), .SS_MOD_PERIOD (SS_MOD_PERIOD) ) phy_top_i ( .ddr3_nrst (SDRST), // output .ddr3_clk (SDCLK), // output .ddr3_nclk (SDNCLK), // output .ddr3_a (SDA[ADDRESS_NUMBER-1:0]), // output[14:0] .ddr3_ba (SDBA[2:0]), // output[2:0] .ddr3_we (SDWE), // output .ddr3_ras (SDRAS), // output .ddr3_cas (SDCAS), // output .ddr3_cke (SDCKE), // output .ddr3_odt (SDODT), // output .dq (SDD[15:0]), // inout[15:0] .dml (SDDML), // inout .dqsl (DQSL), // inout .ndqsl (NDQSL), // inout .dmu (SDDMU), // inout .dqsu (DQSU), // inout .ndqsu (NDQSU), // inout .clk_in (clk_in), // input .clk (), // output .clk_div (clk_div), // output .mclk (mclk), // output .mrst (mrst), // input .ref_clk (ref_clk), // input .idelay_ctrl_reset (idelay_ctrl_reset), // output .rst_in (rst_in), // input .ddr_rst (ddr_rst), // input .dci_rst (dci_rst), // input .dly_rst (dly_rst), // input .in_a (phy_addr[2*ADDRESS_NUMBER-1:0]), // input[29:0] .in_ba (phy_bank[5:0]), // input[5:0] .in_we ({phy_rcw[3],phy_rcw[0]}), // input[1:0] .in_ras ({phy_rcw[5],phy_rcw[2]}), // input[1:0] .in_cas ({phy_rcw[4],phy_rcw[1]}), // input[1:0] .in_cke (phy_cke), // input[1:0] .in_odt (phy_odt), // input[1:0] .in_tri (cmda_tri), // input .din (buf_rdata[63:0]), // input[63:0] .din_dm (dqm_pattern[7:0]), // input[7:0] .tin_dq (phy_dq_tri[7:0]), // input[7:0] .din_dqs (dqs_data), // input[7:0] .tin_dqs (phy_dqs_tri[7:0]), // input[7:0] .dout (phy_rdata[63:0]), // output[63:0] @posedge clk_div .inv_clk_div (inv_clk_div), // input .dci_disable_dqs (phy_dci_dis_dqs), // input .dci_disable_dq (phy_dci_dis_dq), // input .dly_data (dly_data_r), // input[7:0] .dly_addr (dly_addr_r), // input[6:0] .ld_delay (ld_delay_r), // input .set (set_r), // input .locked_mmcm (phy_locked_mmcm), // output .locked_pll (phy_locked_pll), // output .dly_ready (phy_dly_ready), // output .dci_ready (phy_dci_ready), // output .tmp_debug (tmp_debug[7:0]), // output[7:0] .ps_rdy (phy_ps_rdy), // output .ps_out (phy_ps_out) // output[7:0] ); endmodule