litex/verilog/s6ddrphy/s6ddrphy.v

362 lines
7.0 KiB
Coq
Raw Normal View History

2012-02-20 10:13:56 -05:00
/*
2012-02-21 11:38:40 -05:00
* 1:2 frequency-ratio DDR PHY for Spartan-6
2012-02-20 10:13:56 -05:00
*
2012-02-21 11:38:40 -05:00
************* DATAPATH SIGNALS ***********
* Assert dfi_wrdata_en and present the data
* on dfi_wrdata_mask/dfi_wrdata one cycle after
* a write command.
*
* Assert dfi_rddata_en one cycle after a read
* command. The data will come back on dfi_rddata
* 3 cycles later, along with the assertion of
* dfi_rddata_valid.
*
* This PHY only supports CAS Latency 3.
* Read commands must be sent on phase 0.
* Write commands must be sent on phase 1.
2012-02-20 10:13:56 -05:00
*/
2012-02-24 07:54:10 -05:00
2012-02-19 12:43:42 -05:00
module s6ddrphy #(
parameter NUM_AD = 0,
parameter NUM_BA = 0,
parameter NUM_D = 0 /* < number of data lines per DFI phase */
) (
/* Clocks */
input sys_clk,
2012-02-20 17:55:20 -05:00
input clk2x_270,
2012-02-19 12:43:42 -05:00
input clk4x_wr,
input clk4x_wr_strb,
input clk4x_rd,
input clk4x_rd_strb,
/* DFI phase 0 */
input [NUM_AD-1:0] dfi_address_p0,
input [NUM_BA-1:0] dfi_bank_p0,
input dfi_cs_n_p0,
input dfi_cke_p0,
input dfi_ras_n_p0,
input dfi_cas_n_p0,
input dfi_we_n_p0,
input dfi_wrdata_en_p0,
input [NUM_D/8-1:0] dfi_wrdata_mask_p0,
input [NUM_D-1:0] dfi_wrdata_p0,
input dfi_rddata_en_p0,
output [NUM_D-1:0] dfi_rddata_w0,
output dfi_rddata_valid_w0,
/* DFI phase 1 */
input [NUM_AD-1:0] dfi_address_p1,
input [NUM_BA-1:0] dfi_bank_p1,
input dfi_cs_n_p1,
input dfi_cke_p1,
input dfi_ras_n_p1,
input dfi_cas_n_p1,
input dfi_we_n_p1,
input dfi_wrdata_en_p1,
input [NUM_D/8-1:0] dfi_wrdata_mask_p1,
input [NUM_D-1:0] dfi_wrdata_p1,
input dfi_rddata_en_p1,
output [NUM_D-1:0] dfi_rddata_w1,
output dfi_rddata_valid_w1,
/* DDR SDRAM pads */
output sd_clk_out_p,
output sd_clk_out_n,
2012-02-19 14:49:56 -05:00
output reg [NUM_AD-1:0] sd_a,
output reg [NUM_BA-1:0] sd_ba,
output reg sd_cs_n,
output reg sd_cke,
output reg sd_ras_n,
output reg sd_cas_n,
output reg sd_we_n,
2012-02-19 12:43:42 -05:00
inout [NUM_D/2-1:0] sd_dq,
output [NUM_D/16-1:0] sd_dm,
inout [NUM_D/16-1:0] sd_dqs
);
2012-02-19 14:49:56 -05:00
/*
* SDRAM clock
*/
ODDR2 #(
.DDR_ALIGNMENT("NONE"),
.INIT(1'b0),
.SRTYPE("SYNC")
) sd_clk_forward_p (
.Q(sd_clk_out_p),
2012-02-20 17:55:20 -05:00
.C0(clk2x_270),
.C1(~clk2x_270),
2012-02-19 14:49:56 -05:00
.CE(1'b1),
.D0(1'b1),
.D1(1'b0),
.R(1'b0),
.S(1'b0)
);
ODDR2 #(
.DDR_ALIGNMENT("NONE"),
.INIT(1'b0),
.SRTYPE("SYNC")
) sd_clk_forward_n (
.Q(sd_clk_out_n),
2012-02-20 17:55:20 -05:00
.C0(clk2x_270),
.C1(~clk2x_270),
2012-02-19 14:49:56 -05:00
.CE(1'b1),
.D0(1'b0),
.D1(1'b1),
.R(1'b0),
.S(1'b0)
);
/*
* Command/address
*/
reg phase_sel;
2012-02-24 07:54:10 -05:00
always @(posedge clk2x_270)
2012-02-19 14:49:56 -05:00
phase_sel <= sys_clk;
reg [NUM_AD-1:0] r_dfi_address_p0;
reg [NUM_BA-1:0] r_dfi_bank_p0;
reg r_dfi_cs_n_p0;
reg r_dfi_cke_p0;
reg r_dfi_ras_n_p0;
reg r_dfi_cas_n_p0;
reg r_dfi_we_n_p0;
reg [NUM_AD-1:0] r_dfi_address_p1;
reg [NUM_BA-1:0] r_dfi_bank_p1;
reg r_dfi_cs_n_p1;
reg r_dfi_cke_p1;
reg r_dfi_ras_n_p1;
reg r_dfi_cas_n_p1;
reg r_dfi_we_n_p1;
always @(posedge sys_clk) begin
r_dfi_address_p0 <= dfi_address_p0;
r_dfi_bank_p0 <= dfi_bank_p0;
r_dfi_cs_n_p0 <= dfi_cs_n_p0;
r_dfi_cke_p0 <= dfi_cke_p0;
r_dfi_ras_n_p0 <= dfi_ras_n_p0;
r_dfi_cas_n_p0 <= dfi_cas_n_p0;
r_dfi_we_n_p0 <= dfi_we_n_p0;
r_dfi_address_p1 <= dfi_address_p1;
r_dfi_bank_p1 <= dfi_bank_p1;
r_dfi_cs_n_p1 <= dfi_cs_n_p1;
r_dfi_cke_p1 <= dfi_cke_p1;
r_dfi_ras_n_p1 <= dfi_ras_n_p1;
r_dfi_cas_n_p1 <= dfi_cas_n_p1;
r_dfi_we_n_p1 <= dfi_we_n_p1;
end
2012-02-20 17:55:20 -05:00
always @(posedge clk2x_270) begin
2012-02-19 14:49:56 -05:00
if(phase_sel) begin
2012-02-24 07:54:10 -05:00
sd_a <= r_dfi_address_p1;
sd_ba <= r_dfi_bank_p1;
sd_cs_n <= r_dfi_cs_n_p1;
sd_cke <= r_dfi_cke_p1;
sd_ras_n <= r_dfi_ras_n_p1;
sd_cas_n <= r_dfi_cas_n_p1;
sd_we_n <= r_dfi_we_n_p1;
2012-02-20 17:55:20 -05:00
end else begin
2012-02-24 07:54:10 -05:00
sd_a <= r_dfi_address_p0;
sd_ba <= r_dfi_bank_p0;
sd_cs_n <= r_dfi_cs_n_p0;
sd_cke <= r_dfi_cke_p0;
sd_ras_n <= r_dfi_ras_n_p0;
sd_cas_n <= r_dfi_cas_n_p0;
sd_we_n <= r_dfi_we_n_p0;
2012-02-19 14:49:56 -05:00
end
end
2012-02-20 07:45:57 -05:00
/*
* DQ/DQS/DM data
*/
genvar i;
2012-02-20 17:55:20 -05:00
wire drive_dqs;
2012-02-20 07:45:57 -05:00
wire [NUM_D/16-1:0] dqs_o;
wire [NUM_D/16-1:0] dqs_t;
2012-02-20 17:55:20 -05:00
reg postamble;
2012-02-20 07:45:57 -05:00
generate
for(i=0;i<NUM_D/16;i=i+1)
begin: gen_dqs
ODDR2 #(
2012-02-20 10:13:56 -05:00
.DDR_ALIGNMENT("C1"),
2012-02-20 07:45:57 -05:00
.INIT(1'b0),
.SRTYPE("ASYNC")
) dqs_o_oddr (
.Q(dqs_o[i]),
2012-02-20 17:55:20 -05:00
.C0(clk2x_270),
.C1(~clk2x_270),
2012-02-20 07:45:57 -05:00
.CE(1'b1),
.D0(1'b0),
.D1(1'b1),
.R(1'b0),
.S(1'b0)
);
ODDR2 #(
2012-02-20 10:13:56 -05:00
.DDR_ALIGNMENT("C1"),
2012-02-20 07:45:57 -05:00
.INIT(1'b0),
.SRTYPE("ASYNC")
) dqs_t_oddr (
.Q(dqs_t[i]),
2012-02-20 17:55:20 -05:00
.C0(clk2x_270),
.C1(~clk2x_270),
2012-02-20 07:45:57 -05:00
.CE(1'b1),
2012-02-20 17:55:20 -05:00
.D0(~(drive_dqs | postamble)),
.D1(~drive_dqs),
2012-02-20 07:45:57 -05:00
.R(1'b0),
.S(1'b0)
);
OBUFT dqs_obuft(
.I(dqs_o[i]),
.T(dqs_t[i]),
.O(sd_dqs[i])
);
end
endgenerate
2012-02-20 17:55:20 -05:00
always @(posedge clk2x_270)
postamble <= drive_dqs;
2012-02-20 07:45:57 -05:00
2012-02-20 17:55:20 -05:00
wire drive_dq;
2012-02-20 07:45:57 -05:00
wire [NUM_D/2-1:0] dq_i;
wire [NUM_D/2-1:0] dq_o;
wire [NUM_D/2-1:0] dq_t;
generate
for(i=0;i<NUM_D/2;i=i+1)
begin: gen_dq
OSERDES2 #(
.DATA_WIDTH(4),
.DATA_RATE_OQ("SDR"),
.DATA_RATE_OT("SDR"),
.SERDES_MODE("NONE"),
.OUTPUT_MODE("SINGLE_ENDED")
) dq_oserdes (
.OQ(dq_o[i]),
.OCE(1'b1),
.CLK0(clk4x_wr),
.CLK1(1'b0),
.IOCE(clk4x_wr_strb),
2012-02-20 17:55:20 -05:00
.RST(1'b0),
2012-02-20 07:45:57 -05:00
.CLKDIV(sys_clk),
2012-02-20 17:55:20 -05:00
.D1(dfi_wrdata_p0[i+NUM_D/2]),
.D2(dfi_wrdata_p0[i]),
.D3(dfi_wrdata_p1[i+NUM_D/2]),
.D4(dfi_wrdata_p1[i]),
2012-02-20 07:45:57 -05:00
.TQ(dq_t[i]),
2012-02-20 17:55:20 -05:00
.T1(~drive_dq),
.T2(~drive_dq),
.T3(~drive_dq),
.T4(~drive_dq),
2012-02-20 07:45:57 -05:00
.TRAIN(1'b0),
.TCE(1'b1),
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0),
.SHIFTIN3(1'b0),
.SHIFTIN4(1'b0),
.SHIFTOUT1(),
.SHIFTOUT2(),
.SHIFTOUT3(),
.SHIFTOUT4()
);
ISERDES2 #(
.DATA_WIDTH(4),
.DATA_RATE("SDR"),
.BITSLIP_ENABLE("FALSE"),
.SERDES_MODE("NONE"),
.INTERFACE_TYPE("RETIMED")
) dq_iserdes (
.D(dq_i[i]),
.CE0(1'b1),
.CLK0(clk4x_rd),
.CLK1(1'b0),
.IOCE(clk4x_rd_strb),
2012-02-20 17:55:20 -05:00
.RST(1'b0),
2012-02-21 11:38:40 -05:00
.CLKDIV(sys_clk),
2012-02-20 07:45:57 -05:00
.SHIFTIN(),
.BITSLIP(1'b0),
.FABRICOUT(),
2012-02-20 17:55:20 -05:00
.Q1(dfi_rddata_w0[i+NUM_D/2]),
.Q2(dfi_rddata_w0[i]),
.Q3(dfi_rddata_w1[i+NUM_D/2]),
.Q4(dfi_rddata_w1[i]),
2012-02-20 07:45:57 -05:00
.DFB(),
.CFB0(),
.CFB1(),
.VALID(),
.INCDEC(),
.SHIFTOUT()
);
IOBUF dq_iobuf(
.I(dq_o[i]),
.O(dq_i[i]),
.T(dq_t[i]),
.IO(sd_dq[i])
);
end
endgenerate
generate
for(i=0;i<NUM_D/16;i=i+1)
begin: gen_dm_oserdes
OSERDES2 #(
.DATA_WIDTH(4),
.DATA_RATE_OQ("SDR"),
.DATA_RATE_OT("SDR"),
.SERDES_MODE("NONE"),
.OUTPUT_MODE("SINGLE_ENDED")
) dm_oserdes (
.OQ(sd_dm[i]),
.OCE(1'b1),
.CLK0(clk4x_wr),
.CLK1(1'b0),
.IOCE(clk4x_wr_strb),
2012-02-20 17:55:20 -05:00
.RST(1'b0),
2012-02-20 07:45:57 -05:00
.CLKDIV(sys_clk),
2012-02-20 17:55:20 -05:00
.D1(dfi_wrdata_mask_p0[i+NUM_D/16]),
.D2(dfi_wrdata_mask_p0[i]),
.D3(dfi_wrdata_mask_p1[i+NUM_D/16]),
.D4(dfi_wrdata_mask_p1[i]),
2012-02-20 07:45:57 -05:00
.TQ(),
.T1(),
.T2(),
.T3(),
.T4(),
.TRAIN(1'b0),
.TCE(1'b0),
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0),
.SHIFTIN3(1'b0),
.SHIFTIN4(1'b0),
.SHIFTOUT1(),
.SHIFTOUT2(),
.SHIFTOUT3(),
.SHIFTOUT4()
);
end
endgenerate
2012-02-19 14:49:56 -05:00
2012-02-20 07:45:57 -05:00
/*
* DQ/DQS/DM control
*/
2012-02-20 10:13:56 -05:00
reg r_dfi_wrdata_en_p1;
2012-02-21 11:38:40 -05:00
always @(posedge sys_clk)
2012-02-20 10:13:56 -05:00
r_dfi_wrdata_en_p1 <= dfi_wrdata_en_p1;
reg r2_dfi_wrdata_en_p1;
2012-02-21 11:38:40 -05:00
always @(posedge clk2x_270)
2012-02-20 10:13:56 -05:00
r2_dfi_wrdata_en_p1 <= r_dfi_wrdata_en_p1;
2012-02-21 11:38:40 -05:00
assign drive_dqs = r2_dfi_wrdata_en_p1;
assign drive_dq = dfi_wrdata_en_p1;
2012-02-20 07:45:57 -05:00
2012-02-21 11:38:40 -05:00
wire rddata_valid;
reg [3:0] rddata_sr;
assign dfi_rddata_valid_w0 = rddata_sr[0];
assign dfi_rddata_valid_w1 = rddata_sr[0];
always @(posedge sys_clk)
rddata_sr <= {dfi_rddata_en_p0, rddata_sr[3:1]};
2012-02-20 07:45:57 -05:00
2012-02-19 12:43:42 -05:00
endmodule