Prepare for new DDR PHY

This commit is contained in:
Sebastien Bourdeauducq 2012-02-19 18:43:42 +01:00
parent 1e4e092a55
commit f35cd4a85b
15 changed files with 146 additions and 536 deletions

2
.gitignore vendored
View file

@ -6,5 +6,3 @@ build/*
tools/bin2hex
tools/flterm
tools/mkmmimg
verilog/s6ddrphy/*.v
verilog/s6ddrphy/.pc

View file

@ -21,7 +21,7 @@ build/soc-routed.ncd: build/soc.ncd
cd build && par -ol high -w soc.ncd soc-routed.ncd
build/soc.bit build/soc.bin: build/soc-routed.ncd
cd build && bitgen -g Binary:Yes -g INIT_9K:Yes -w soc-routed.ncd soc.bit
cd build && bitgen -g LCK_cycle:6 -g Binary:Yes -g INIT_9K:Yes -w soc-routed.ncd soc.bit
load: build/soc.bit
jtag -n load.jtag

19
README
View file

@ -19,22 +19,6 @@ production version of Milkymist SoC, visit:
First, download and install Migen from:
https://github.com/milkymist/migen
Then, you will need to fetch the "Spartan-6 FPGA DDR/DDR2 SDRAM PHY core"
(PHY only solution, we do not need the NWL memory controller) from:
http://www.xilinx.com/products/intellectual-property/1-1MFEDB.htm
Downloading it is free of charge, but it cannot be redistributed in
source form due to copyright restrictions.
Place the Verilog source code of the PHY (contents of
phy_rtl/spartan6_soft_phy) into the verilog/s6ddrphy folder.
Then run (from verilog/s6ddrphy):
quilt push -a
in order to apply patches that make the PHY more compliant with the DFI
specification in general, and in particular with the capability to send
multiple SDRAM commands in one system clock cycle, which our new SDRAM
controller is capable of doing.
The patches are against version 1.04 of the PHY.
Once this is done, build the bitstream with:
python3 build.py
This will generate the build/soc.bit programming file.
@ -67,8 +51,7 @@ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
The authors grant the additional permissions that the code can be used in
conjunction with the LatticeMico32 CPU core from Lattice and the
Spartan-6 FPGA DDR/DDR2 SDRAM PHY core from Xilinx and Northwest Logic.
conjunction with the LatticeMico32 CPU core from Lattice.
Unless otherwise noted, Milkymist-NG's source code is copyright (C)
2011-2012 Sebastien Bourdeauducq. Other authors retain ownership of their

View file

@ -13,7 +13,6 @@ def get(ns, crg0, norflash0, uart0, ddrphy0):
add(crg0.ac97_rst_n, "D6")
add(crg0.videoin_rst_n, "W17")
add(crg0.flash_rst_n, "P22", extra="SLEW = FAST | DRIVE = 8")
add(crg0.rd_clk_lb, "K5", extra="IOSTANDARD = SSTL2_I")
add(crg0.trigger_reset, "AA4")
add_vec(norflash0.adr, ["L22", "L20", "K22", "K21", "J19", "H20", "F22",
@ -61,15 +60,8 @@ def get(ns, crg0, norflash0, uart0, ddrphy0):
r += """
TIMESPEC "TSclk50" = PERIOD "GRPclk50" 20 ns HIGH 50%;
INST "spartan6_soft_phy/datapath_s6_inst/dq_idelay_cal_inst/max_tap_drp" LOC = "IODELAY_X0Y79"; # use sd_dm[0] at E1
INST "m1crg/wr_bufpll_left" LOC = "BUFPLL_X0Y2";
INST "m1crg/wr_bufpll_right" LOC = "BUFPLL_X2Y2";
INST "m1crg/rd_bufpll_left" LOC = "BUFPLL_X0Y3";
INST "m1crg/rd_bufpll_right" LOC = "BUFPLL_X2Y3";
# MAP (13.4) hallucinates that this placement is unroutable. Tell it to STFU.
PIN "m1crg/rd_bufpll_left.IOCLK" CLOCK_DEDICATED_ROUTE = FALSE;
PIN "spartan6_soft_phy/datapath_s6_inst/dq_idelay_cal_inst/max_tap_drp.IOCLK0" CLOCK_DEDICATED_ROUTE = FALSE;
INST "m1crg/wr_bufpll" LOC = "BUFPLL_X0Y2";
INST "m1crg/rd_bufpll" LOC = "BUFPLL_X0Y3";
"""
return r

View file

@ -15,21 +15,15 @@ class M1CRG:
"videoin_rst_n",
"flash_rst_n",
"clk2x_90",
"clk4x_wr_left",
"clk4x_wr_strb_left",
"clk4x_wr_right",
"clk4x_wr_strb_right",
"clk4x_rd_left",
"clk4x_rd_strb_left",
"clk4x_rd_right",
"clk4x_rd_strb_right"
"clk4x_wr",
"clk4x_wr_strb",
"clk4x_rd",
"clk4x_rd_strb"
]:
s = Signal(name=name)
setattr(self, name, s)
generated.append((name, s))
self.rd_clk_lb = Signal()
ratio = Fraction(outfreq1x)/Fraction(infreq)
in_period = float(Fraction(1000000000)/Fraction(infreq))
@ -38,9 +32,8 @@ class M1CRG:
[
("clkin", self.clkin),
("trigger_reset", self.trigger_reset)
], [
("rd_clk_lb", self.rd_clk_lb)
], [
],
parameters=[
("in_period", in_period),
("f_mult", ratio.numerator),
("f_div", ratio.denominator)
@ -49,4 +42,4 @@ class M1CRG:
def get_fragment(self):
return Fragment(instances=[self._inst],
pads={self.clkin, self.ac97_rst_n, self.videoin_rst_n, self.flash_rst_n, self.rd_clk_lb})
pads={self.clkin, self.ac97_rst_n, self.videoin_rst_n, self.flash_rst_n})

View file

@ -1,31 +1,24 @@
from migen.fhdl.structure import *
from migen.bus import dfi
from migen.bank.description import *
from migen.bank import csrgen
class S6DDRPHY:
def __init__(self, csr_address, a, ba, d):
def __init__(self, a, ba, d):
ins = []
outs = []
inouts = []
for name in [
"clk2x_90",
"clk4x_wr_left",
"clk4x_wr_strb_left",
"clk4x_wr_right",
"clk4x_wr_strb_right",
"clk4x_rd_left",
"clk4x_rd_strb_left",
"clk4x_rd_right",
"clk4x_rd_strb_right"
"clk4x_wr",
"clk4x_wr_strb",
"clk4x_rd",
"clk4x_rd_strb"
]:
s = Signal(name=name)
setattr(self, name, s)
ins.append((name, s))
self._sd_pins = []
sd_d = d//4
for name, width, l in [
("sd_clk_out_p", 1, outs),
("sd_clk_out_n", 1, outs),
@ -36,9 +29,9 @@ class S6DDRPHY:
("sd_ras_n", 1, outs),
("sd_cas_n", 1, outs),
("sd_we_n", 1, outs),
("sd_dq", sd_d, inouts),
("sd_dm", sd_d//8, outs),
("sd_dqs", sd_d//8, inouts)
("sd_dq", d//2, inouts),
("sd_dm", d//16, outs),
("sd_dqs", d//16, inouts)
]:
s = Signal(BV(width), name=name)
@ -46,93 +39,20 @@ class S6DDRPHY:
l.append((name, s))
self._sd_pins.append(s)
self.dfi = dfi.Interface(a, ba, d)
self.dfi = dfi.Interface(a, ba, d, 2)
ins += self.dfi.get_standard_names(True, False)
outs += self.dfi.get_standard_names(False, True)
ins += [
("reset_n", BV(1)),
("cfg_al", BV(3)),
("cfg_cl", BV(3)),
("cfg_bl", BV(2)),
("cfg_regdimm", BV(1)),
("init_done", BV(1)),
("cpg_busy", BV(1)),
("diag_dq_recal", BV(1)),
("diag_io_sel", BV(9)),
("diag_disable_cal_on_startup", BV(1)),
("diag_cal_bits", BV(2)),
("diag_short_cal", BV(1))
]
outs += [
("phy_cal_done", BV(1)),
("cpg_r_req", BV(1)),
("cpg_w_req", BV(1)),
("cpg_addr", BV(a)),
("cpg_b_size", BV(4))
]
self._inst = Instance("spartan6_soft_phy",
self._inst = Instance("s6ddrphy",
outs,
ins,
inouts,
[
("DSIZE", d),
("NUM_AD", a),
("NUM_BA", ba),
("ADDR_WIDTH", 31),
("DQ_IO_LOC", Constant(2**32-1, BV(32))),
("DM_IO_LOC", Constant(2**4-1, BV(4)))
("NUM_D", d)
],
clkport="clk")
self._reset_n = Field("reset_n")
self._init_done = Field("init_done")
self._phy_cal_done = Field("phy_cal_done", 1, READ_ONLY, WRITE_ONLY)
self._status = RegisterFields("status",
[self._reset_n, self._init_done, self._phy_cal_done])
self._req = RegisterRaw("req", 2)
self._req_addr = RegisterField("req_addr", 8, READ_ONLY, WRITE_ONLY)
self.bank = csrgen.Bank([self._status, self._req, self._req_addr],
address=csr_address)
clkport="sys_clk")
def get_fragment(self):
pending_r = Signal()
pending_w = Signal()
cpg_busy = Signal()
comb = [
self._inst.ins["cfg_al"].eq(0),
self._inst.ins["cfg_cl"].eq(3),
self._inst.ins["cfg_bl"].eq(1),
self._inst.ins["cfg_regdimm"].eq(0),
self._inst.ins["diag_dq_recal"].eq(0),
self._inst.ins["diag_io_sel"].eq(0),
self._inst.ins["diag_disable_cal_on_startup"].eq(0),
self._inst.ins["diag_cal_bits"].eq(0),
self._inst.ins["diag_short_cal"].eq(0),
self._inst.ins["reset_n"].eq(self._reset_n.r),
self._inst.ins["init_done"].eq(self._init_done.r),
self._phy_cal_done.w.eq(self._inst.outs["phy_cal_done"]),
self._req_addr.field.w.eq(self._inst.outs["cpg_addr"][2:10]),
self._req.w.eq(Cat(pending_r, pending_w)),
cpg_busy.eq(pending_r | pending_w),
self._inst.ins["cpg_busy"].eq(cpg_busy)
]
sync = [
If(self._inst.outs["cpg_r_req"], pending_r.eq(1)),
If(self._inst.outs["cpg_w_req"], pending_w.eq(1)),
If(self._req.re & self._req.r[0], pending_r.eq(0)),
If(self._req.re & self._req.r[1], pending_w.eq(0))
]
return Fragment(comb, sync, instances=[self._inst], pads=set(self._sd_pins)) \
+ self.bank.get_fragment()
return Fragment(instances=[self._inst], pads=set(self._sd_pins))

View file

@ -17,7 +17,6 @@
#include <stdio.h>
#include <hw/s6ddrphy.h>
#include <hw/dfii.h>
#include "ddrinit.h"
@ -79,54 +78,11 @@ static void init_sequence(void)
cdelay(200);
}
static void calibrate_phy(void)
{
int requests;
int addr;
printf("Calibrating PHY...\n");
CSR_DFII_WRDELAY = 4;
CSR_DFII_WRDURATION = 1;
CSR_DFII_RDDELAY = 7;
CSR_DFII_RDDURATION = 1;
/* Use bank 0, activate row 0 */
CSR_DFII_BA = 0;
setaddr(0x0000);
CSR_DFII_COMMAND = DFII_COMMAND_RAS|DFII_COMMAND_CS;
while(!(CSR_DDRPHY_STATUS & DDRPHY_STATUS_PHY_CAL_DONE)) {
cdelay(20);
requests = CSR_DDRPHY_REQUESTS;
addr = CSR_DDRPHY_REQADDR;
setaddr(addr << 2);
if(requests & DDRPHY_REQUEST_READ) {
printf("R %d\n", addr);
CSR_DFII_COMMAND = DFII_COMMAND_RDDATA|DFII_COMMAND_CAS|DFII_COMMAND_CS;
}
if(requests & DDRPHY_REQUEST_WRITE) {
printf("W %d\n", addr);
CSR_DFII_COMMAND = DFII_COMMAND_WRDATA|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS;
}
CSR_DDRPHY_REQUESTS = requests;
}
/* Precharge All */
setaddr(0x0400);
CSR_DFII_COMMAND = DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS;
}
int ddrinit(void)
{
printf("Initializing DDR SDRAM...\n");
CSR_DDRPHY_STATUS = DDRPHY_STATUS_RESETN;
init_sequence();
CSR_DDRPHY_STATUS = DDRPHY_STATUS_RESETN|DDRPHY_STATUS_INIT_DONE;
calibrate_phy();
return 1;
}

View file

@ -20,7 +20,7 @@
#include <hw/common.h>
#define CSR_DFII_CONTROL MMPTR(0xe0001000)
#define CSR_DFII_CONTROL MMPTR(0xe0000800)
#define DFII_CONTROL_SEL (0x01)
#define DFII_CONTROL_CKE (0x02)
@ -34,13 +34,13 @@
#define DFII_COMMAND_RDDATA (0x10)
#define DFII_COMMAND_WRDATA (0x20)
#define CSR_DFII_AH MMPTR(0xe0001008)
#define CSR_DFII_AL MMPTR(0xe000100C)
#define CSR_DFII_BA MMPTR(0xe0001010)
#define CSR_DFII_AH MMPTR(0xe0000808)
#define CSR_DFII_AL MMPTR(0xe000080C)
#define CSR_DFII_BA MMPTR(0xe0000810)
#define CSR_DFII_RDDELAY MMPTR(0xe0001014)
#define CSR_DFII_RDDURATION MMPTR(0xe0001018)
#define CSR_DFII_WRDELAY MMPTR(0xe000101C)
#define CSR_DFII_WRDURATION MMPTR(0xe0001020)
#define CSR_DFII_RDDELAY MMPTR(0xe0000814)
#define CSR_DFII_RDDURATION MMPTR(0xe0000818)
#define CSR_DFII_WRDELAY MMPTR(0xe000081C)
#define CSR_DFII_WRDURATION MMPTR(0xe0000820)
#endif /* __HW_DFII_H */

View file

@ -1,36 +0,0 @@
/*
* Milkymist SoC (Software)
* Copyright (C) 2012 Sebastien Bourdeauducq
*
* This program 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, version 3 of the License.
*
* This program 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __HW_S6DDRPHY_H
#define __HW_S6DDRPHY_H
#include <hw/common.h>
#define CSR_DDRPHY_STATUS MMPTR(0xe0000800)
#define DDRPHY_STATUS_RESETN (0x1)
#define DDRPHY_STATUS_INIT_DONE (0x2)
#define DDRPHY_STATUS_PHY_CAL_DONE (0x4)
#define CSR_DDRPHY_REQUESTS MMPTR(0xe0000804)
#define DDRPHY_REQUEST_READ (0x1)
#define DDRPHY_REQUEST_WRITE (0x2)
#define CSR_DDRPHY_REQADDR MMPTR(0xe0000808)
#endif /* __HW_S6DDRPHY_H */

19
top.py
View file

@ -14,19 +14,15 @@ l2_size = 8192 # in bytes
dfi_a = 13
dfi_ba = 2
dfi_d = 128 # TODO -> 64
dfi_d = 64
def ddrphy_clocking(crg, phy):
names = [
"clk2x_90",
"clk4x_wr_left",
"clk4x_wr_strb_left",
"clk4x_wr_right",
"clk4x_wr_strb_right",
"clk4x_rd_left",
"clk4x_rd_strb_left",
"clk4x_rd_right",
"clk4x_rd_strb_right",
"clk4x_wr",
"clk4x_wr_strb",
"clk4x_rd",
"clk4x_rd_strb"
]
comb = [getattr(phy, name).eq(getattr(crg, name)) for name in names]
return Fragment(comb)
@ -42,8 +38,8 @@ def get():
#
# DFI
#
ddrphy0 = s6ddrphy.S6DDRPHY(1, dfi_a, dfi_ba, dfi_d)
dfii0 = dfii.DFIInjector(2, dfi_a, dfi_ba, dfi_d, 1)
ddrphy0 = s6ddrphy.S6DDRPHY(dfi_a, dfi_ba, dfi_d)
dfii0 = dfii.DFIInjector(1, dfi_a, dfi_ba, dfi_d, 2)
dficon0 = dfi.Interconnect(dfii0.master, ddrphy0.dfi)
#
@ -80,7 +76,6 @@ def get():
uart0 = uart.UART(0, clk_freq, baud=115200)
csrcon0 = csr.Interconnect(wishbone2csr0.csr, [
uart0.bank.interface,
ddrphy0.bank.interface,
dfii0.bank.interface
])

View file

@ -32,37 +32,25 @@ module m1crg #(
output videoin_rst_n,
output flash_rst_n,
/* DDR PHY clocks and reset */
/* DDR PHY clocks */
output clk2x_90,
output clk4x_wr_left,
output clk4x_wr_strb_left,
output clk4x_wr_right,
output clk4x_wr_strb_right,
output clk4x_rd_left,
output clk4x_rd_strb_left,
output clk4x_rd_right,
output clk4x_rd_strb_right,
inout rd_clk_lb /* < unconnected clock pin for read clock PLL loopback */
output clk4x_wr,
output clk4x_wr_strb,
output clk4x_rd,
output clk4x_rd_strb
);
/*
* Reset
*/
wire reset_n;
reg [19:0] rst_debounce;
always @(posedge sys_clk, negedge reset_n) begin
if(~reset_n) begin
always @(posedge sys_clk) begin
if(trigger_reset)
rst_debounce <= 20'hFFFFF;
sys_rst <= 1'b1;
end else begin
if(trigger_reset)
rst_debounce <= 20'hFFFFF;
else if(rst_debounce != 20'd0)
rst_debounce <= rst_debounce - 20'd1;
sys_rst <= rst_debounce != 20'd0;
end
else if(rst_debounce != 20'd0)
rst_debounce <= rst_debounce - 20'd1;
sys_rst <= rst_debounce != 20'd0;
end
assign ac97_rst_n = ~sys_rst;
@ -79,23 +67,19 @@ assign videoin_rst_n = ~sys_rst;
reg [7:0] flash_rstcounter;
always @(posedge sys_clk, negedge reset_n) begin
if(~reset_n) begin
always @(posedge sys_clk) begin
if(trigger_reset)
flash_rstcounter <= 8'd0;
end else begin
if(trigger_reset)
flash_rstcounter <= 8'd0;
else if(~flash_rstcounter[7])
flash_rstcounter <= flash_rstcounter + 8'd1;
end
else if(~flash_rstcounter[7])
flash_rstcounter <= flash_rstcounter + 8'd1;
end
assign flash_rst_n = flash_rstcounter[7];
/*
* Clock management. Largely taken from the NWL reference design.
* Clock management. Inspired by the NWL reference design.
*/
wire sdr_clkin;
wire clkdiv;
@ -117,12 +101,12 @@ BUFIO2 #(
.SERDESSTROBE()
);
wire pll1_lckd;
wire buf_pll1_fb_out;
wire pll1out0;
wire pll1out1;
wire pll1out2;
wire pll1out3;
wire pll_lckd;
wire buf_pll_fb_out;
wire pllout0;
wire pllout1;
wire pllout2;
wire pllout3;
PLL_ADV #(
.BANDWIDTH("OPTIMIZED"),
@ -136,12 +120,12 @@ PLL_ADV #(
.CLKOUT1_DIVIDE(f_div),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT1_PHASE(0),
.CLKOUT2_DIVIDE(4*f_div),
.CLKOUT2_DIVIDE(2*f_div),
.CLKOUT2_DUTY_CYCLE(0.5),
.CLKOUT2_PHASE(0.0),
.CLKOUT3_DIVIDE(2*f_div),
.CLKOUT2_PHASE(90.0),
.CLKOUT3_DIVIDE(4*f_div),
.CLKOUT3_DUTY_CYCLE(0.5),
.CLKOUT3_PHASE(90),
.CLKOUT3_PHASE(0.0),
.CLKOUT4_DIVIDE(7),
.CLKOUT4_DUTY_CYCLE(0.5),
.CLKOUT4_PHASE(0),
@ -153,13 +137,13 @@ PLL_ADV #(
.REF_JITTER(0.100),
.CLK_FEEDBACK("CLKFBOUT"),
.SIM_DEVICE("SPARTAN6")
) pll1 (
) pll (
.CLKFBDCM(),
.CLKFBOUT(buf_pll1_fb_out),
.CLKOUT0(pll1out0), /* < x4 180 clock for transmitter */
.CLKOUT1(pll1out1), /* < x4 180 clock for transmitter */
.CLKOUT2(pll1out2), /* < x1 clock for memory controller */
.CLKOUT3(pll1out3), /* < x2 90 clock to generate memory clock, clock DQS and memory address and control signals. */
.CLKFBOUT(buf_pll_fb_out),
.CLKOUT0(pllout0), /* < x4 clock for writes */
.CLKOUT1(pllout1), /* < x4 clock for reads */
.CLKOUT2(pllout2), /* < x2 90 clock to generate memory clock, clock DQS and memory address and control signals. */
.CLKOUT3(pllout3), /* < x1 clock for system and memory controller */
.CLKOUT4(),
.CLKOUT5(),
.CLKOUTDCM0(),
@ -170,8 +154,8 @@ PLL_ADV #(
.CLKOUTDCM5(),
.DO(),
.DRDY(),
.LOCKED(pll1_lckd),
.CLKFBIN(buf_pll1_fb_out),
.LOCKED(pll_lckd),
.CLKFBIN(buf_pll_fb_out),
.CLKIN1(clkdiv),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
@ -186,239 +170,34 @@ PLL_ADV #(
BUFPLL #(
.DIVIDE(4)
) wr_bufpll_left (
.PLLIN(pll1out0),
) wr_bufpll (
.PLLIN(pllout0),
.GCLK(sys_clk),
.LOCKED(pll1_lckd),
.IOCLK(clk4x_wr_left),
.LOCKED(pll_lckd),
.IOCLK(clk4x_wr),
.LOCK(),
.SERDESSTROBE(clk4x_wr_strb_left)
.SERDESSTROBE(clk4x_wr_strb)
);
BUFPLL #(
.DIVIDE(4)
) wr_bufpll_right (
.PLLIN(pll1out1),
) rd_bufpll (
.PLLIN(pllout1),
.GCLK(sys_clk),
.LOCKED(pll1_lckd),
.IOCLK(clk4x_wr_right),
.LOCKED(pll_lckd),
.IOCLK(clk4x_rd),
.LOCK(),
.SERDESSTROBE(clk4x_wr_strb_right)
);
BUFG bufg_x1(
.I(pll1out2),
.O(sys_clk)
.SERDESSTROBE(clk4x_rd_strb)
);
BUFG bufg_x2_2(
.I(pll1out3),
.I(pllout2),
.O(clk2x_90)
);
/*
* Generate clk4x_rd. This clock is sourced from clk2x_90.
* An IODELAY2 element is included in the path of this clock so that
* any variation in IDELAY element's base delay is compensated when this clock
* is used to capture read data which also goes through IDELAY element.
*/
wire rd_clk_out;
ODDR2 #(
.DDR_ALIGNMENT("C0"),
.INIT(1'b0),
.SRTYPE("ASYNC")
) rd_clk_out_inst (
.Q(rd_clk_out),
.C0(clk2x_90),
.C1(~clk2x_90),
.CE(1'b1),
.D0(1'b1),
.D1(1'b0),
.R(1'b0),
.S(1'b0)
BUFG bufg_x1(
.I(pllout3),
.O(sys_clk)
);
wire rd_clk_out_oe_n;
ODDR2 #(
.DDR_ALIGNMENT("C0"),
.INIT(1'b0),
.SRTYPE("ASYNC")
) rd_clk_out_oe_inst (
.Q(rd_clk_out_oe_n),
.C0(clk2x_90),
.C1(~clk2x_90),
.CE(1'b1),
.D0(1'b0),
.D1(1'b0),
.R(1'b0),
.S(1'b0)
);
wire rd_clk_fb;
/* Dummy pin used for calibration */
IOBUF rd_clk_loop_back_inst(
.O(rd_clk_fb),
.IO(rd_clk_lb),
.I(rd_clk_out),
.T(rd_clk_out_oe_n)
);
wire rd_clk_fb_dly;
IODELAY2 #(
.DATA_RATE("DDR"),
.IDELAY_VALUE(0),
.IDELAY2_VALUE(0),
.IDELAY_MODE("NORMAL"),
.ODELAY_VALUE(0),
.IDELAY_TYPE("FIXED"),
.COUNTER_WRAPAROUND("STAY_AT_LIMIT"),
.DELAY_SRC("IDATAIN"),
.SERDES_MODE("NONE"),
.SIM_TAPDELAY_VALUE(49)
) iodelay_cm (
.IDATAIN(rd_clk_fb),
.TOUT(),
.DOUT(),
.T(1'b1),
.ODATAIN(1'b0),
.DATAOUT(rd_clk_fb_dly),
.DATAOUT2(),
.IOCLK0(1'b0),
.IOCLK1(1'b0),
.CLK(1'b0),
.CAL(1'b0),
.INC(1'b0),
.CE(1'b0),
.RST(1'b0),
.BUSY()
);
wire rd_clk_fb_dly_bufio;
BUFIO2 #(
.DIVIDE(1),
.DIVIDE_BYPASS("FALSE"),
.I_INVERT("FALSE")
) bufio2_inst (
.I(rd_clk_fb_dly),
.IOCLK(),
.DIVCLK(rd_clk_fb_dly_bufio),
.SERDESSTROBE()
);
wire pll2_lckd;
wire buf_pll2_fb_out;
wire pll2out0;
wire pll2out1;
PLL_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT(4),
.CLKFBOUT_PHASE(0.0),
.CLKIN1_PERIOD(clk2x_period),
.CLKIN2_PERIOD(clk2x_period),
.CLKOUT0_DIVIDE(2),
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0.0),
.CLKOUT1_DIVIDE(2),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT1_PHASE(0.0),
.CLKOUT2_DIVIDE(7),
.CLKOUT2_DUTY_CYCLE(0.5),
.CLKOUT2_PHASE(0.0),
.CLKOUT3_DIVIDE(7),
.CLKOUT3_DUTY_CYCLE(0.5),
.CLKOUT3_PHASE(0.0),
.CLKOUT4_DIVIDE(7),
.CLKOUT4_DUTY_CYCLE(0.5),
.CLKOUT4_PHASE(0.0),
.CLKOUT5_DIVIDE(7),
.CLKOUT5_DUTY_CYCLE (0.5),
.CLKOUT5_PHASE(0.0),
.COMPENSATION("INTERNAL"),
.DIVCLK_DIVIDE(1),
.REF_JITTER(0.100),
.CLK_FEEDBACK("CLKFBOUT"),
.SIM_DEVICE("SPARTAN6")
) pll2 (
.CLKFBDCM(),
.CLKFBOUT(buf_pll2_fb_out),
.CLKOUT0(pll2out0), /* < x4 clock to capture read data */
.CLKOUT1(pll2out1), /* < x4 clock to capture read data */
.CLKOUT2(),
.CLKOUT3(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUTDCM0(),
.CLKOUTDCM1(),
.CLKOUTDCM2(),
.CLKOUTDCM3(),
.CLKOUTDCM4(),
.CLKOUTDCM5(),
.DO(),
.DRDY(),
.LOCKED(pll2_lckd),
.CLKFBIN(buf_pll2_fb_out),
.CLKIN1(rd_clk_fb_dly_bufio),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.DADDR(5'b00000),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'h0000),
.DWE(1'b0),
.RST(~pll1_lckd),
.REL(1'b0)
);
BUFPLL #(
.DIVIDE(4)
) rd_bufpll_left (
.PLLIN(pll2out0),
.GCLK(sys_clk),
.LOCKED(pll2_lckd),
.IOCLK(clk4x_rd_left),
.LOCK(),
.SERDESSTROBE(clk4x_rd_strb_left)
);
BUFPLL #(
.DIVIDE(4)
) rd_bufpll_right (
.PLLIN(pll2out1),
.GCLK(sys_clk),
.LOCKED(pll2_lckd),
.IOCLK(clk4x_rd_right),
.LOCK(),
.SERDESSTROBE(clk4x_rd_strb_right)
);
wire sdram_sys_clk_lock_d16;
reg sdram_sys_clk_lock_d17;
/*
* Async reset generation
* The reset is de-asserted 16 clocks after both internal clocks are locked.
*/
SRL16 reset_delay_sr(
.CLK(sys_clk),
.D(pll1_lckd & pll2_lckd),
.A0(1'b1),
.A1(1'b1),
.A2(1'b1),
.A3(1'b1),
.Q(sdram_sys_clk_lock_d16)
);
always @(posedge sys_clk)
sdram_sys_clk_lock_d17 <= sdram_sys_clk_lock_d16;
assign reset_n = sdram_sys_clk_lock_d17;
endmodule

View file

@ -1 +0,0 @@
The Verilog files of the Spartan-6 DDR PHY from Xilinx/Northwest Logic go here.

View file

@ -1,27 +0,0 @@
Index: s6ddrphy/spartan6_soft_phy.v
===================================================================
--- s6ddrphy.orig/spartan6_soft_phy.v
+++ s6ddrphy/spartan6_soft_phy.v
@@ -116,7 +116,6 @@ module spartan6_soft_phy # (
inout [NUM_DQ-1:0] sd_dq, // Data in from SDRAM device
output [NUM_DQS-1:0] sd_dm, // Data mask to SDRAM devices
inout [NUM_DQS-1:0] sd_dqs, // DQS
- inout [NUM_DQS-1:0] sd_dqs_n, // complimentary DQS
// configuration ports
input [2:0] cfg_al, // Posted CAS additive latency
@@ -300,12 +299,11 @@ genvar j;
generate
for (j = 0; j < NUM_DQ/8*(NIBBLE_DEVICES+1) ; j = j + 1)
begin:dqs_iob
- IOBUFDS iobufds (
+ IOBUF iobufds (
.O (sd_dqs_in[j]),
.I (sd_dqs_out[j]),
.T (sd_dqs_oe_n[j]),
- .IO (sd_dqs[j]),
- .IOB (sd_dqs_n[j])
+ .IO (sd_dqs[j])
);
end
endgenerate

View file

@ -1 +0,0 @@
s6ddrphy.diff

View file

@ -0,0 +1,59 @@
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,
input clk2x_90,
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,
output [NUM_AD-1:0] sd_a,
output [NUM_BA-1:0] sd_ba,
output sd_cs_n,
output sd_cke,
output sd_ras_n,
output sd_cas_n,
output sd_we_n,
inout [NUM_D/2-1:0] sd_dq,
output [NUM_D/16-1:0] sd_dm,
inout [NUM_D/16-1:0] sd_dqs
);
endmodule