//---------------------------------------------------------------------------- // Pipelined, asyncronous DDR Controller // // (c) Joerg Bornschein () //---------------------------------------------------------------------------- module ddr_ctrl #( parameter phase_shift = 0, parameter clk_freq = 100000000, parameter clk_multiply = 12, parameter clk_divide = 5, parameter wait200_init = 26 ) ( input clk, input reset, // DDR ports output [2:0] ddr_clk, output [2:0] ddr_clk_n, input ddr_clk_fb, output ddr_ras_n, output ddr_cas_n, output ddr_we_n, output [1:0] ddr_cke, output [1:0] ddr_cs_n, output [ `A_RNG] ddr_a, output [ `BA_RNG] ddr_ba, inout [ `DQ_RNG] ddr_dq, inout [`DQS_RNG] ddr_dqs, output [ `DM_RNG] ddr_dm, // FML (FastMemoryLink) output reg fml_done, input [`FML_ADR_RNG] fml_adr, input fml_rd, input fml_wr, input [`FML_DAT_RNG] fml_wdat, input [`FML_BE_RNG] fml_wbe, input fml_wnext, output fml_rempty, input fml_rnext, output [`FML_DAT_RNG] fml_rdat, // DCM phase shift control output ps_ready, input ps_up, input ps_down, // Logic Probe output probe_clk, input [7:0] probe_sel, output reg [7:0] probe ); wire [ `DQ_RNG] ddr_dq_i, ddr_dq_o; wire [`DQS_RNG] ddr_dqs_i, ddr_dqs_o; wire ddr_dqs_oe; //---------------------------------------------------------------------------- // clock generator //---------------------------------------------------------------------------- wire clk_locked; wire write_clk, write_clk90; wire read_clk; wire reset_int = reset | ~clk_locked; ddr_clkgen #( .phase_shift( phase_shift ), .clk_multiply( clk_multiply ), .clk_divide( clk_divide ) ) clkgen ( .clk( clk ), .reset( reset ), .locked( clk_locked ), // ddr-clk .read_clk( read_clk ), .write_clk( write_clk ), .write_clk90( write_clk90 ), // phase shift control .ps_ready( ps_ready ), .ps_up( ps_up ), .ps_down( ps_down ) ); //---------------------------------------------------------------------------- // async_fifos (cmd, wdata, rdata) //---------------------------------------------------------------------------- wire cba_fifo_full; reg [`CBA_RNG] cba_fifo_din; reg cba_fifo_we; wire wfifo_full; wire [`WFIFO_RNG] wfifo_din; wire wfifo_we; wire [`RFIFO_RNG] rfifo_dout; wire rfifo_empty; wire rfifo_next; assign wfifo_din = { ~fml_wbe, fml_wdat }; assign wfifo_we = fml_wnext; assign fml_rdat = rfifo_dout; assign fml_rempty = rfifo_empty; assign rfifo_next = fml_rnext; //---------------------------------------------------------------------------- // High-speed cmd, write and read datapath //---------------------------------------------------------------------------- ddr_wpath wpath0 ( .clk( write_clk ), .clk90( write_clk90 ), .reset( reset_int ), // CBA async fifo .cba_clk( clk ), .cba_din( cba_fifo_din ), .cba_wr( cba_fifo_we ), .cba_full( cba_fifo_full ), // WDATA async fifo .wdata_clk( clk ), .wdata_din( wfifo_din ), .wdata_wr( wfifo_we ), .wdata_full( wfifo_full ), // .sample( sample ), // DDR .ddr_clk( ddr_clk ), .ddr_clk_n( ddr_clk_n ), .ddr_ras_n( ddr_ras_n ), .ddr_cas_n( ddr_cas_n ), .ddr_we_n( ddr_we_n ), .ddr_a( ddr_a ), .ddr_ba( ddr_ba ), .ddr_dm( ddr_dm ), .ddr_dq( ddr_dq_o ), .ddr_dqs( ddr_dqs_o ), .ddr_dqs_oe( ddr_dqs_oe ) ); ddr_rpath rpath0 ( .clk( read_clk ), .reset( reset_int ), // .sample( sample ), // .rfifo_clk( clk ), .rfifo_empty( rfifo_empty), .rfifo_dout( rfifo_dout ), .rfifo_next( rfifo_next ), // DDR .ddr_dq( ddr_dq_i ), .ddr_dqs( ddr_dqs_i ) ); //---------------------------------------------------------------------------- // 7.8 us pulse generator //---------------------------------------------------------------------------- wire pulse78; reg ar_req; reg ar_done; ddr_pulse78 #( .clk_freq( clk_freq ) ) pulse78_gen ( .clk( clk ), .reset( reset_int ), .pulse78( pulse78 ) ); //---------------------------------------------------------------------------- // Auto Refresh request generator //---------------------------------------------------------------------------- always @(posedge clk) if (reset_int) ar_req <= 0; else ar_req <= pulse78 | (ar_req & ~ar_done); // operations we might want to submit wire [`CBA_RNG] ar_pre_cba; wire [`CBA_RNG] ar_ar_cba; assign ar_pre_cba = { `DDR_CMD_PRE, 2'b00, 13'b1111111111111 }; assign ar_ar_cba = { `DDR_CMD_AR, 2'b00, 13'b0000000000000 }; //---------------------------------------------------------------------------- // Init & management //---------------------------------------------------------------------------- wire init_req; reg init_ack; wire [`CBA_RNG] init_cba; wire init_done; wire wait200; ddr_init #( .wait200_init( wait200_init ) ) init ( .clk( clk ), .reset( reset_int ), .pulse78( pulse78 ), .wait200( wait200 ), .init_done( init_done ), // .mngt_req( init_req ), .mngt_ack( init_ack ), .mngt_cba( init_cba ) ); //---------------------------------------------------------------------------- // Active Bank Information //---------------------------------------------------------------------------- reg [`ROW_RNG] ba_row [3:0]; reg [3:0] ba_active; //---------------------------------------------------------------------------- // FML decoding //---------------------------------------------------------------------------- wire [`FML_ADR_BA_RNG] fml_ba = fml_adr[`FML_ADR_BA_RNG]; wire [`FML_ADR_ROW_RNG] fml_row = fml_adr[`FML_ADR_ROW_RNG]; wire [`FML_ADR_COL_RNG] fml_col = fml_adr[`FML_ADR_COL_RNG]; wire [`FML_ADR_ROW_RNG] fml_cur_row; // current active row in sel. bank assign fml_cur_row = ba_row[fml_ba]; wire fml_row_active; // is row in selected ba really active? assign fml_row_active = ba_active[fml_ba]; /* wire fml_row_active = (fml_ba == 0) ? ba0_active : // is row in selected (fml_ba == 1) ? ba1_active : // bank really active? (fml_ba == 2) ? ba2_active : ba3_active ; */ // request operation iff correct bank is active wire fml_req = fml_rd | fml_wr; wire fml_row_match = (fml_row == fml_cur_row) & fml_row_active; wire fml_pre_req = fml_req & ~fml_row_match & fml_row_active; wire fml_act_req = fml_req & ~fml_row_active; wire fml_read_req = fml_rd & fml_row_match & ~fml_done; wire fml_write_req = fml_wr & fml_row_match & ~fml_done; // actual operations we might want to submit wire [`CBA_RNG] fml_pre_cba; wire [`CBA_RNG] fml_act_cba; wire [`CBA_RNG] fml_read_cba; wire [`CBA_RNG] fml_write_cba; assign fml_pre_cba = { `DDR_CMD_PRE, fml_ba, 13'b0 }; assign fml_act_cba = { `DDR_CMD_ACT, fml_ba, fml_row }; assign fml_read_cba = { `DDR_CMD_READ, fml_ba, {3'b000}, fml_col, {3'b000} }; assign fml_write_cba = { `DDR_CMD_WRITE, fml_ba, {3'b000}, fml_col, {3'b000} }; //---------------------------------------------------------------------------- // Schedule and issue commands //---------------------------------------------------------------------------- parameter s_init = 0; parameter s_idle = 1; parameter s_ar = 2; parameter s_reading = 3; reg [1:0] state; always @(posedge clk) begin if (reset_int) begin state <= s_init; ba_active <= 0; ba_row[0] <= 0; ba_row[1] <= 0; ba_row[2] <= 0; ba_row[3] <= 0; fml_done <= 0; init_ack <= 0; cba_fifo_we <= 0; ar_done <= 0; end else begin fml_done <= 0; init_ack <= 0; cba_fifo_we <= 0; ar_done <= 0; case (state) s_init: begin if (init_done) state <= s_idle; if (init_req & ~cba_fifo_full) begin cba_fifo_we <= 1; cba_fifo_din <= init_cba; init_ack <= 1; end end s_idle: begin if (fml_read_req & ~cba_fifo_full) begin cba_fifo_we <= 1; cba_fifo_din <= fml_read_cba; fml_done <= 1; end else if (fml_write_req & ~cba_fifo_full) begin cba_fifo_we <= 1; cba_fifo_din <= fml_write_cba; fml_done <= 1; end else if (ar_req & ~cba_fifo_full) begin cba_fifo_we <= 1; cba_fifo_din <= ar_pre_cba; ar_done <= 1; ba_active <= 'b0; state <= s_ar; end else if (fml_pre_req & ~cba_fifo_full) begin cba_fifo_we <= 1; cba_fifo_din <= fml_pre_cba; ba_active[fml_ba] <= 0; end else if (fml_act_req & ~cba_fifo_full) begin cba_fifo_we <= 1; cba_fifo_din <= fml_act_cba; ba_active[fml_ba] <= 1; ba_row[fml_ba] <= fml_row; end end s_ar: begin if (~cba_fifo_full) begin cba_fifo_we <= 1; cba_fifo_din <= ar_ar_cba; state <= s_idle; end end endcase end end //---------------------------------------------------------------------------- // Demux dqs and dq //---------------------------------------------------------------------------- assign ddr_cke = {~wait200, ~wait200}; // bring up CKE as soon 200us wait is finished assign ddr_dqs = ddr_dqs_oe!=1'b0 ? ddr_dqs_o : 'bz; assign ddr_dq = ddr_dqs_oe!=1'b0 ? ddr_dq_o : 'bz; assign ddr_dqs_i = ddr_dqs; assign ddr_dq_i = ddr_dq; assign ddr_cs_n = 2'b00; //---------------------------------------------------------------------------- // Probes //---------------------------------------------------------------------------- assign probe_clk = clk; always @(*) begin case (probe_sel) 8'h00: probe <= { cba_fifo_we, wfifo_we, rfifo_next, 1'b0, cba_fifo_full, wfifo_full, rfifo_empty, 1'b0 }; 8'h01: probe <= { write_clk, write_clk90, read_clk, 5'b00000 }; 8'h10: probe <= { rfifo_empty, rfifo_next, rfifo_dout[ 5: 0] }; 8'h11: probe <= { rfifo_empty, rfifo_next, rfifo_dout[13: 8] }; 8'h12: probe <= { rfifo_empty, rfifo_next, rfifo_dout[21:16] }; 8'h13: probe <= { rfifo_empty, rfifo_next, rfifo_dout[29:24] }; 8'h20: probe <= wfifo_din[ 7:0]; 8'h21: probe <= wfifo_din[15:8]; 8'h20: probe <= wfifo_din[23:16]; 8'h21: probe <= wfifo_din[31:24]; 8'h30: probe <= cba_fifo_din[17:10]; 8'h31: probe <= cba_fifo_din[ 9:2]; default: probe <= 0'b0; endcase end endmodule