litex/misoc/cores/sdram_phy/s6ddrphy.py

488 lines
18 KiB
Python

# 1:2 and 1:4 frequency-ratio DDR / LPDDR / DDR2 / DDR3 PHYs for Spartan-6
#
# Assert dfi_wrdata_en and present the data
# on dfi_wrdata_mask/dfi_wrdata in the same
# cycle as the write command.
#
# Assert dfi_rddata_en in the same cycle as the read
# command. The data will come back on dfi_rddata
# 5 cycles later, along with the assertion
# of dfi_rddata_valid.
#
# This PHY only supports CAS latency 3 for DDR, LPDDR, DDR2
# and CAS latency 5/CAS write latency 6 for DDR3.
#
# Read commands must be sent on phase 0.
# Write commands must be sent on phase 1.
#
from functools import reduce
from operator import or_
from migen import *
from migen.genlib.record import *
from misoc.interconnect.dfi import *
from misoc.cores import sdram_settings
class S6HalfRateDDRPHY(Module):
def __init__(self, pads, memtype, rd_bitslip, wr_bitslip, dqs_ddr_alignment):
if memtype not in ["DDR", "LPDDR", "DDR2", "DDR3"]:
raise NotImplementedError("S6HalfRateDDRPHY only supports DDR, LPDDR, DDR2 and DDR3")
addressbits = len(pads.a)
bankbits = len(pads.ba)
databits = len(pads.dq)
nphases = 2
if memtype == "DDR3":
self.settings = sdram_settings.PhySettings(
memtype="DDR3",
dfi_databits=2*databits,
nphases=nphases,
rdphase=0,
wrphase=1,
rdcmdphase=1,
wrcmdphase=0,
cl=5,
cwl=6,
read_latency=6,
write_latency=2
)
else:
self.settings = sdram_settings.PhySettings(
memtype=memtype,
dfi_databits=2*databits,
nphases=nphases,
rdphase=0,
wrphase=1,
rdcmdphase=1,
wrcmdphase=0,
cl=3,
read_latency=5,
write_latency=0
)
self.dfi = Interface(addressbits, bankbits, 2*databits, nphases)
self.clk4x_wr_strb = Signal()
self.clk4x_rd_strb = Signal()
###
# sys_clk : system clk, used for dfi interface
# sdram_half_clk : half rate sdram clk
# sdram_full_wr_clk : full rate sdram write clk
# sdram_full_rd_clk : full rate sdram read clk
sd_sys = getattr(self.sync, "sys")
sd_sdram_half = getattr(self.sync, "sdram_half")
sys_clk = ClockSignal("sys")
sdram_half_clk = ClockSignal("sdram_half")
sdram_full_wr_clk = ClockSignal("sdram_full_wr")
sdram_full_rd_clk = ClockSignal("sdram_full_rd")
#
# Command/address
#
# select active phase
# sys_clk ----____----____
# phase_sel(nphases=2) 0 1 0 1 Half Rate
phase_sel = Signal(log2_int(nphases))
phase_half = Signal.like(phase_sel)
phase_sys = Signal.like(phase_half)
sd_sys += phase_sys.eq(phase_half)
sd_sdram_half += [
If(phase_half == phase_sys,
phase_sel.eq(0),
).Else(
phase_sel.eq(phase_sel+1)
),
phase_half.eq(phase_half+1),
]
# register dfi cmds on half_rate clk
r_dfi = Array(Record(phase_cmd_description(addressbits, bankbits)) for i in range(nphases))
for n, phase in enumerate(self.dfi.phases):
sd_sdram_half += [
r_dfi[n].reset_n.eq(phase.reset_n),
r_dfi[n].odt.eq(phase.odt),
r_dfi[n].address.eq(phase.address),
r_dfi[n].bank.eq(phase.bank),
r_dfi[n].cs_n.eq(phase.cs_n),
r_dfi[n].cke.eq(phase.cke),
r_dfi[n].cas_n.eq(phase.cas_n),
r_dfi[n].ras_n.eq(phase.ras_n),
r_dfi[n].we_n.eq(phase.we_n)
]
# output cmds
sd_sdram_half += [
pads.a.eq(r_dfi[phase_sel].address),
pads.ba.eq(r_dfi[phase_sel].bank),
pads.cke.eq(r_dfi[phase_sel].cke),
pads.ras_n.eq(r_dfi[phase_sel].ras_n),
pads.cas_n.eq(r_dfi[phase_sel].cas_n),
pads.we_n.eq(r_dfi[phase_sel].we_n)
]
# optional pads
for name in "reset_n", "cs_n", "odt":
if hasattr(pads, name):
sd_sdram_half += getattr(pads, name).eq(getattr(r_dfi[phase_sel], name))
#
# Bitslip
#
bitslip_cnt = Signal(4)
bitslip_inc = Signal()
sd_sys += [
If(bitslip_cnt == rd_bitslip,
bitslip_inc.eq(0)
).Else(
bitslip_cnt.eq(bitslip_cnt+1),
bitslip_inc.eq(1)
)
]
#
# DQ/DQS/DM data
#
sdram_half_clk_n = Signal()
self.comb += sdram_half_clk_n.eq(~sdram_half_clk)
postamble = Signal()
drive_dqs = Signal()
dqs_t_d0 = Signal()
dqs_t_d1 = Signal()
dqs_o = Signal(databits//8)
dqs_t = Signal(databits//8)
self.comb += [
dqs_t_d0.eq(~(drive_dqs | postamble)),
dqs_t_d1.eq(~drive_dqs),
]
for i in range(databits//8):
# DQS output
self.specials += Instance("ODDR2",
p_DDR_ALIGNMENT=dqs_ddr_alignment,
p_INIT=0,
p_SRTYPE="ASYNC",
i_C0=sdram_half_clk,
i_C1=sdram_half_clk_n,
i_CE=1,
i_D0=0,
i_D1=1,
i_R=0,
i_S=0,
o_Q=dqs_o[i]
)
# DQS tristate cmd
self.specials += Instance("ODDR2",
p_DDR_ALIGNMENT=dqs_ddr_alignment,
p_INIT=0,
p_SRTYPE="ASYNC",
i_C0=sdram_half_clk,
i_C1=sdram_half_clk_n,
i_CE=1,
i_D0=dqs_t_d0,
i_D1=dqs_t_d1,
i_R=0,
i_S=0,
o_Q=dqs_t[i]
)
# DQS tristate buffer
if hasattr(pads, "dqs_n"):
self.specials += Instance("OBUFTDS",
i_I=dqs_o[i],
i_T=dqs_t[i],
o_O=pads.dqs[i],
o_OB=pads.dqs_n[i],
)
else:
self.specials += Instance("OBUFT",
i_I=dqs_o[i],
i_T=dqs_t[i],
o_O=pads.dqs[i]
)
sd_sdram_half += postamble.eq(drive_dqs)
d_dfi = [Record(phase_wrdata_description(nphases*databits)+phase_rddata_description(nphases*databits))
for i in range(2*nphases)]
for n, phase in enumerate(self.dfi.phases):
self.comb += [
d_dfi[n].wrdata.eq(phase.wrdata),
d_dfi[n].wrdata_mask.eq(phase.wrdata_mask),
d_dfi[n].wrdata_en.eq(phase.wrdata_en),
d_dfi[n].rddata_en.eq(phase.rddata_en),
]
sd_sys += [
d_dfi[nphases+n].wrdata.eq(phase.wrdata),
d_dfi[nphases+n].wrdata_mask.eq(phase.wrdata_mask)
]
drive_dq = Signal()
drive_dq_n = [Signal() for i in range(2)]
self.comb += drive_dq_n[0].eq(~drive_dq)
sd_sys += drive_dq_n[1].eq(drive_dq_n[0])
dq_t = Signal(databits)
dq_o = Signal(databits)
dq_i = Signal(databits)
dq_wrdata = []
for i in range(2):
for j in reversed(range(nphases)):
dq_wrdata.append(d_dfi[i*nphases+j].wrdata[:databits])
dq_wrdata.append(d_dfi[i*nphases+j].wrdata[databits:])
for i in range(databits):
# Data serializer
self.specials += Instance("OSERDES2",
p_DATA_WIDTH=4,
p_DATA_RATE_OQ="SDR",
p_DATA_RATE_OT="SDR",
p_SERDES_MODE="NONE",
p_OUTPUT_MODE="SINGLE_ENDED",
o_OQ=dq_o[i],
i_OCE=1,
i_CLK0=sdram_full_wr_clk,
i_CLK1=0,
i_IOCE=self.clk4x_wr_strb,
i_RST=0,
i_CLKDIV=sys_clk,
i_D1=dq_wrdata[wr_bitslip+3][i],
i_D2=dq_wrdata[wr_bitslip+2][i],
i_D3=dq_wrdata[wr_bitslip+1][i],
i_D4=dq_wrdata[wr_bitslip+0][i],
o_TQ=dq_t[i],
i_T1=drive_dq_n[(wr_bitslip+3)//4],
i_T2=drive_dq_n[(wr_bitslip+2)//4],
i_T3=drive_dq_n[(wr_bitslip+1)//4],
i_T4=drive_dq_n[(wr_bitslip+0)//4],
i_TRAIN=0,
i_TCE=1,
i_SHIFTIN1=0,
i_SHIFTIN2=0,
i_SHIFTIN3=0,
i_SHIFTIN4=0,
)
# Data deserializer
self.specials += Instance("ISERDES2",
p_DATA_WIDTH=4,
p_DATA_RATE="SDR",
p_BITSLIP_ENABLE="TRUE",
p_SERDES_MODE="NONE",
p_INTERFACE_TYPE="RETIMED",
i_D=dq_i[i],
i_CE0=1,
i_CLK0=sdram_full_rd_clk,
i_CLK1=0,
i_IOCE=self.clk4x_rd_strb,
i_RST=ResetSignal(),
i_CLKDIV=sys_clk,
i_BITSLIP=bitslip_inc,
o_Q1=d_dfi[0*nphases+0].rddata[i+databits],
o_Q2=d_dfi[0*nphases+0].rddata[i],
o_Q3=d_dfi[0*nphases+1].rddata[i+databits],
o_Q4=d_dfi[0*nphases+1].rddata[i],
)
# Data buffer
self.specials += Instance("IOBUF",
i_I=dq_o[i],
o_O=dq_i[i],
i_T=dq_t[i],
io_IO=pads.dq[i]
)
dq_wrdata_mask = []
for i in range(2):
for j in reversed(range(nphases)):
dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[:databits//8])
dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[databits//8:])
for i in range(databits//8):
# Mask serializer
self.specials += Instance("OSERDES2",
p_DATA_WIDTH=4,
p_DATA_RATE_OQ="SDR",
p_DATA_RATE_OT="SDR",
p_SERDES_MODE="NONE",
p_OUTPUT_MODE="SINGLE_ENDED",
o_OQ=pads.dm[i],
i_OCE=1,
i_CLK0=sdram_full_wr_clk,
i_CLK1=0,
i_IOCE=self.clk4x_wr_strb,
i_RST=0,
i_CLKDIV=sys_clk,
i_D1=dq_wrdata_mask[wr_bitslip+3][i],
i_D2=dq_wrdata_mask[wr_bitslip+2][i],
i_D3=dq_wrdata_mask[wr_bitslip+1][i],
i_D4=dq_wrdata_mask[wr_bitslip+0][i],
i_TRAIN=0,
i_TCE=0,
i_SHIFTIN1=0,
i_SHIFTIN2=0,
i_SHIFTIN3=0,
i_SHIFTIN4=0,
)
#
# DQ/DQS/DM control
#
# write
wrdata_en = Signal()
self.comb += wrdata_en.eq(reduce(or_, [d_dfi[p].wrdata_en for p in range(nphases)]))
if memtype == "DDR3":
r_drive_dq = Signal(self.settings.cwl-1)
sd_sdram_half += r_drive_dq.eq(Cat(wrdata_en, r_drive_dq))
self.comb += drive_dq.eq(r_drive_dq[self.settings.cwl-2])
else:
self.comb += drive_dq.eq(wrdata_en)
wrdata_en_d = Signal()
sd_sys += wrdata_en_d.eq(wrdata_en)
r_dfi_wrdata_en = Signal(max(self.settings.cwl, self.settings.cl))
sd_sdram_half += r_dfi_wrdata_en.eq(Cat(wrdata_en_d, r_dfi_wrdata_en))
if memtype == "DDR3":
self.comb += drive_dqs.eq(r_dfi_wrdata_en[self.settings.cwl-1])
else:
self.comb += drive_dqs.eq(r_dfi_wrdata_en[1])
# read
rddata_en = Signal()
self.comb += rddata_en.eq(reduce(or_, [d_dfi[p].rddata_en for p in range(nphases)]))
rddata_sr = Signal(self.settings.read_latency)
sd_sys += rddata_sr.eq(Cat(rddata_sr[1:self.settings.read_latency], rddata_en))
for n, phase in enumerate(self.dfi.phases):
self.comb += [
phase.rddata.eq(d_dfi[n].rddata),
phase.rddata_valid.eq(rddata_sr[0]),
]
class S6QuarterRateDDRPHY(Module):
def __init__(self, pads, rd_bitslip, wr_bitslip, dqs_ddr_alignment):
half_rate_phy = S6HalfRateDDRPHY(pads, "DDR3", rd_bitslip, wr_bitslip, dqs_ddr_alignment)
self.submodules += RenameClockDomains(half_rate_phy, {"sys" : "sys2x"})
addressbits = len(pads.a)
bankbits = len(pads.ba)
databits = len(pads.dq)
nphases = 4
self.settings = sdram_settings.PhySettings(
memtype="DDR3",
dfi_databits=2*databits,
nphases=nphases,
rdphase=0,
wrphase=1,
rdcmdphase=1,
wrcmdphase=0,
cl=5,
cwl=6,
read_latency=6//2+1,
write_latency=2//2
)
self.dfi = Interface(addressbits, bankbits, 2*databits, nphases)
self.clk8x_wr_strb = half_rate_phy.clk4x_wr_strb
self.clk8x_rd_strb = half_rate_phy.clk4x_rd_strb
# sys_clk : system clk, used for dfi interface
# sys2x_clk : 2x system clk
sd_sys = getattr(self.sync, "sys")
sd_sys2x = getattr(self.sync, "sys2x")
# select active sys2x phase
# sys_clk ----____----____
# phase_sel 0 1 0 1
phase_sel = Signal()
phase_sys2x = Signal.like(phase_sel)
phase_sys = Signal.like(phase_sys2x)
sd_sys += phase_sys.eq(phase_sys2x)
sd_sys2x += [
If(phase_sys2x == phase_sys,
phase_sel.eq(0),
).Else(
phase_sel.eq(~phase_sel)
),
phase_sys2x.eq(~phase_sel)
]
# DFI adaptation
# Commands and writes
dfi_leave_out = set(["rddata", "rddata_valid", "wrdata_en"])
self.comb += [
If(~phase_sel,
Record.connect(self.dfi.phases[0], half_rate_phy.dfi.phases[0], leave_out=dfi_leave_out),
Record.connect(self.dfi.phases[1], half_rate_phy.dfi.phases[1], leave_out=dfi_leave_out),
).Else(
Record.connect(self.dfi.phases[2], half_rate_phy.dfi.phases[0], leave_out=dfi_leave_out),
Record.connect(self.dfi.phases[3], half_rate_phy.dfi.phases[1], leave_out=dfi_leave_out),
),
]
wr_data_en = self.dfi.phases[self.settings.wrphase].wrdata_en & ~phase_sel
wr_data_en_d = Signal()
sd_sys2x += wr_data_en_d.eq(wr_data_en)
self.comb += half_rate_phy.dfi.phases[half_rate_phy.settings.wrphase].wrdata_en.eq(wr_data_en | wr_data_en_d)
# Reads
rddata = Array(Signal(2*databits) for i in range(2))
rddata_valid = Signal(2)
for i in range(2):
sd_sys2x += [
rddata_valid[i].eq(half_rate_phy.dfi.phases[i].rddata_valid),
rddata[i].eq(half_rate_phy.dfi.phases[i].rddata)
]
sd_sys += [
self.dfi.phases[0].rddata.eq(rddata[0]),
self.dfi.phases[0].rddata_valid.eq(rddata_valid[0]),
self.dfi.phases[1].rddata.eq(rddata[1]),
self.dfi.phases[1].rddata_valid.eq(rddata_valid[1]),
self.dfi.phases[2].rddata.eq(half_rate_phy.dfi.phases[0].rddata),
self.dfi.phases[2].rddata_valid.eq(half_rate_phy.dfi.phases[0].rddata_valid),
self.dfi.phases[3].rddata.eq(half_rate_phy.dfi.phases[1].rddata),
self.dfi.phases[3].rddata_valid.eq(half_rate_phy.dfi.phases[1].rddata_valid)
]