1
0
mirror of git://projects.qi-hardware.com/nn-usb-fpga.git synced 2025-01-11 01:00:15 +02:00
nn-usb-fpga/lm32/logic/sakc/rtl/wb_ddr/ddr_ctrl.v
2010-05-25 21:49:58 -05:00

371 lines
11 KiB
Verilog

//----------------------------------------------------------------------------
// Pipelined, asyncronous DDR Controller
//
// (c) Joerg Bornschein (<jb@capsec.org>)
//----------------------------------------------------------------------------
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