s6ddrphy: cleanup
This commit is contained in:
parent
da2b7aa961
commit
ea65aaaddd
|
@ -67,7 +67,7 @@ def get_csr_header(csr_base, bank_array, interrupt_map):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def get_sdram_phy_header(sdram_phy):
|
def get_sdram_phy_header(sdram_phy):
|
||||||
if sdram_phy.phy_settings.type not in ["SDR", "DDR", "LPDDR", "DDR2"]:
|
if sdram_phy.phy_settings.memtype not in ["SDR", "DDR", "LPDDR", "DDR2"]:
|
||||||
raise NotImplementedError("The SDRAM PHY header generator only supports SDR, DDR, LPDDR and DDR2")
|
raise NotImplementedError("The SDRAM PHY header generator only supports SDR, DDR, LPDDR and DDR2")
|
||||||
|
|
||||||
r = "#ifndef __HW_SDRAM_PHY_H\n#define __HW_SDRAM_PHY_H\n"
|
r = "#ifndef __HW_SDRAM_PHY_H\n#define __HW_SDRAM_PHY_H\n"
|
||||||
|
@ -129,7 +129,7 @@ static void command_p{n}(int cmd)
|
||||||
|
|
||||||
cl = sdram_phy.phy_settings.cl
|
cl = sdram_phy.phy_settings.cl
|
||||||
|
|
||||||
if sdram_phy.phy_settings.type == "SDR":
|
if sdram_phy.phy_settings.memtype == "SDR":
|
||||||
bl = 1*sdram_phy.phy_settings.nphases
|
bl = 1*sdram_phy.phy_settings.nphases
|
||||||
mr = log2_int(bl) + (cl << 4)
|
mr = log2_int(bl) + (cl << 4)
|
||||||
reset_dll = 1 << 8
|
reset_dll = 1 << 8
|
||||||
|
@ -144,7 +144,7 @@ static void command_p{n}(int cmd)
|
||||||
("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200)
|
("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200)
|
||||||
]
|
]
|
||||||
|
|
||||||
elif sdram_phy.phy_settings.type == "DDR":
|
elif sdram_phy.phy_settings.memtype == "DDR":
|
||||||
bl = 2*sdram_phy.phy_settings.nphases
|
bl = 2*sdram_phy.phy_settings.nphases
|
||||||
mr = log2_int(bl) + (cl << 4)
|
mr = log2_int(bl) + (cl << 4)
|
||||||
emr = 0
|
emr = 0
|
||||||
|
@ -161,7 +161,7 @@ static void command_p{n}(int cmd)
|
||||||
("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200)
|
("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200)
|
||||||
]
|
]
|
||||||
|
|
||||||
elif sdram_phy.phy_settings.type == "LPDDR":
|
elif sdram_phy.phy_settings.memtype == "LPDDR":
|
||||||
bl = 2*sdram_phy.phy_settings.nphases
|
bl = 2*sdram_phy.phy_settings.nphases
|
||||||
mr = log2_int(bl) + (cl << 4)
|
mr = log2_int(bl) + (cl << 4)
|
||||||
emr = 0
|
emr = 0
|
||||||
|
@ -178,7 +178,7 @@ static void command_p{n}(int cmd)
|
||||||
("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200)
|
("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200)
|
||||||
]
|
]
|
||||||
|
|
||||||
elif sdram_phy.phy_settings.type == "DDR2":
|
elif sdram_phy.phy_settings.memtype == "DDR2":
|
||||||
bl = 2*sdram_phy.phy_settings.nphases
|
bl = 2*sdram_phy.phy_settings.nphases
|
||||||
mr = log2_int(bl) + (cl << 4)
|
mr = log2_int(bl) + (cl << 4)
|
||||||
emr = 0
|
emr = 0
|
||||||
|
|
|
@ -7,14 +7,13 @@ from milkymist.lasmicon.refresher import *
|
||||||
from milkymist.lasmicon.bankmachine import *
|
from milkymist.lasmicon.bankmachine import *
|
||||||
from milkymist.lasmicon.multiplexer import *
|
from milkymist.lasmicon.multiplexer import *
|
||||||
|
|
||||||
PhySettings = namedtuple("PhySettings", "type dfi_d nphases rdphase wrphase cl")
|
PhySettings = namedtuple("PhySettings", "memtype dfi_d nphases rdphase wrphase cl read_latency write_latency")
|
||||||
|
|
||||||
class GeomSettings(namedtuple("_GeomSettings", "bank_a row_a col_a")):
|
class GeomSettings(namedtuple("_GeomSettings", "bank_a row_a col_a")):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.mux_a = max(self.row_a, self.col_a)
|
self.mux_a = max(self.row_a, self.col_a)
|
||||||
|
|
||||||
TimingSettings = namedtuple("TimingSettings", "tRP tRCD tWR tWTR tREFI tRFC" \
|
TimingSettings = namedtuple("TimingSettings", "tRP tRCD tWR tWTR tREFI tRFC" \
|
||||||
" read_latency write_latency" \
|
|
||||||
" req_queue_size read_time write_time")
|
" req_queue_size read_time write_time")
|
||||||
|
|
||||||
class LASMIcon(Module):
|
class LASMIcon(Module):
|
||||||
|
@ -31,8 +30,8 @@ class LASMIcon(Module):
|
||||||
dw=phy_settings.dfi_d*phy_settings.nphases,
|
dw=phy_settings.dfi_d*phy_settings.nphases,
|
||||||
nbanks=2**geom_settings.bank_a,
|
nbanks=2**geom_settings.bank_a,
|
||||||
req_queue_size=timing_settings.req_queue_size,
|
req_queue_size=timing_settings.req_queue_size,
|
||||||
read_latency=timing_settings.read_latency+1,
|
read_latency=phy_settings.read_latency+1,
|
||||||
write_latency=timing_settings.write_latency+1)
|
write_latency=phy_settings.write_latency+1)
|
||||||
self.nrowbits = geom_settings.col_a - address_align
|
self.nrowbits = geom_settings.col_a - address_align
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
|
@ -178,7 +178,7 @@ class Multiplexer(Module, AutoCSR):
|
||||||
steerer.sel[0].eq(STEER_REFRESH),
|
steerer.sel[0].eq(STEER_REFRESH),
|
||||||
If(~refresher.req, NextState("READ"))
|
If(~refresher.req, NextState("READ"))
|
||||||
)
|
)
|
||||||
fsm.delayed_enter("RTW", "WRITE", timing_settings.read_latency-1)
|
fsm.delayed_enter("RTW", "WRITE", phy_settings.read_latency-1) # FIXME: reduce this, actual limit is around (cl+1)/nphases
|
||||||
fsm.delayed_enter("WTR", "READ", timing_settings.tWTR-1)
|
fsm.delayed_enter("WTR", "READ", timing_settings.tWTR-1)
|
||||||
# FIXME: workaround for zero-delay loop simulation problem with Icarus Verilog
|
# FIXME: workaround for zero-delay loop simulation problem with Icarus Verilog
|
||||||
fsm.finalize()
|
fsm.finalize()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#
|
|
||||||
# 1:2 frequency-ratio DDR / LPDDR / DDR2 PHY for
|
# 1:2 frequency-ratio DDR / LPDDR / DDR2 PHY for
|
||||||
# Spartan-6
|
# Spartan-6
|
||||||
#
|
#
|
||||||
|
@ -12,37 +11,42 @@
|
||||||
# of dfi_rddata_valid.
|
# of dfi_rddata_valid.
|
||||||
#
|
#
|
||||||
# This PHY only supports CAS Latency 3.
|
# This PHY only supports CAS Latency 3.
|
||||||
# Read commands must be sent on phase RDPHASE.
|
# Read commands must be sent on phase 0.
|
||||||
# Write commands must be sent on phase WRPHASE.
|
# Write commands must be sent on phase 1.
|
||||||
#/
|
#
|
||||||
|
|
||||||
# Todo:
|
# Todo:
|
||||||
# - use CSR for bitslip?
|
# - use CSR for bitslip?
|
||||||
# - add configurable CAS Latency
|
# - add configurable CAS Latency
|
||||||
# - automatically determines wrphase / rdphase / latencies according to phy_settings
|
# - automatically determines wrphase / rdphase / latencies
|
||||||
|
|
||||||
from migen.fhdl.std import *
|
from migen.fhdl.std import *
|
||||||
from migen.bus.dfi import *
|
from migen.bus.dfi import *
|
||||||
from migen.genlib.record import *
|
from migen.genlib.record import *
|
||||||
|
|
||||||
def get_latencies(phy_settings):
|
from milkymist import lasmicon
|
||||||
read_latency=5
|
|
||||||
write_latency=0
|
|
||||||
return read_latency, write_latency
|
|
||||||
|
|
||||||
class S6DDRPHY(Module):
|
class S6DDRPHY(Module):
|
||||||
def __init__(self, pads, phy_settings, bitslip):
|
def __init__(self, pads, memtype, nphases, cl, bitslip):
|
||||||
if phy_settings.type not in ["DDR", "LPDDR", "DDR2"]:
|
if memtype not in ["DDR", "LPDDR", "DDR2"]:
|
||||||
raise NotImplementedError("S6DDRPHY only supports DDR, LPDDR and DDR2")
|
raise NotImplementedError("S6DDRPHY only supports DDR, LPDDR and DDR2")
|
||||||
if phy_settings.cl != 3:
|
if cl != 3:
|
||||||
raise NotImplementedError("S6DDRPHY only supports CAS LATENCY 3 for now")
|
raise NotImplementedError("S6DDRPHY only supports CAS LATENCY 3")
|
||||||
|
|
||||||
a = flen(pads.a)
|
a = flen(pads.a)
|
||||||
ba = flen(pads.ba)
|
ba = flen(pads.ba)
|
||||||
d = flen(pads.dq)
|
d = flen(pads.dq)
|
||||||
nphases = phy_settings.nphases
|
|
||||||
self.phy_settings = phy_settings
|
self.phy_settings = lasmicon.PhySettings(
|
||||||
read_latency, write_latency = get_latencies(phy_settings)
|
memtype=memtype,
|
||||||
|
dfi_d=2*d,
|
||||||
|
nphases=nphases,
|
||||||
|
rdphase=0,
|
||||||
|
wrphase=1,
|
||||||
|
cl=cl,
|
||||||
|
read_latency=5,
|
||||||
|
write_latency=0
|
||||||
|
)
|
||||||
|
|
||||||
self.dfi = Interface(a, ba, nphases*d, nphases)
|
self.dfi = Interface(a, ba, nphases*d, nphases)
|
||||||
self.clk4x_wr_strb = Signal()
|
self.clk4x_wr_strb = Signal()
|
||||||
|
@ -112,7 +116,7 @@ class S6DDRPHY(Module):
|
||||||
bitslip_inc = Signal()
|
bitslip_inc = Signal()
|
||||||
|
|
||||||
sd_sys += [
|
sd_sys += [
|
||||||
If(bitslip_cnt==bitslip,
|
If(bitslip_cnt == bitslip,
|
||||||
bitslip_inc.eq(0)
|
bitslip_inc.eq(0)
|
||||||
).Else(
|
).Else(
|
||||||
bitslip_cnt.eq(bitslip_cnt+1),
|
bitslip_cnt.eq(bitslip_cnt+1),
|
||||||
|
@ -156,7 +160,7 @@ class S6DDRPHY(Module):
|
||||||
Instance.Input("S", 0),
|
Instance.Input("S", 0),
|
||||||
|
|
||||||
Instance.Output("Q", dqs_o[i])
|
Instance.Output("Q", dqs_o[i])
|
||||||
)
|
)
|
||||||
|
|
||||||
# DQS tristate cmd
|
# DQS tristate cmd
|
||||||
self.specials += Instance("ODDR2",
|
self.specials += Instance("ODDR2",
|
||||||
|
@ -174,7 +178,7 @@ class S6DDRPHY(Module):
|
||||||
Instance.Input("S", 0),
|
Instance.Input("S", 0),
|
||||||
|
|
||||||
Instance.Output("Q", dqs_t[i])
|
Instance.Output("Q", dqs_t[i])
|
||||||
)
|
)
|
||||||
|
|
||||||
# DQS tristate buffer
|
# DQS tristate buffer
|
||||||
self.specials += Instance("OBUFT",
|
self.specials += Instance("OBUFT",
|
||||||
|
@ -182,7 +186,7 @@ class S6DDRPHY(Module):
|
||||||
Instance.Input("T", dqs_t[i]),
|
Instance.Input("T", dqs_t[i]),
|
||||||
|
|
||||||
Instance.Output("O", pads.dqs[i])
|
Instance.Output("O", pads.dqs[i])
|
||||||
)
|
)
|
||||||
|
|
||||||
sd_sdram_half += postamble.eq(drive_dqs)
|
sd_sdram_half += postamble.eq(drive_dqs)
|
||||||
|
|
||||||
|
@ -333,26 +337,26 @@ class S6DDRPHY(Module):
|
||||||
Instance.Output("SHIFTOUT4"),
|
Instance.Output("SHIFTOUT4"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# DQ/DQS/DM control
|
# DQ/DQS/DM control
|
||||||
#
|
#
|
||||||
self.comb += drive_dq.eq(d_dfi[phy_settings.wrphase].wrdata_en)
|
self.comb += drive_dq.eq(d_dfi[self.phy_settings.wrphase].wrdata_en)
|
||||||
sd_sys += d_drive_dq.eq(drive_dq)
|
sd_sys += d_drive_dq.eq(drive_dq)
|
||||||
|
|
||||||
d_dfi_wrdata_en = Signal()
|
d_dfi_wrdata_en = Signal()
|
||||||
sd_sys += d_dfi_wrdata_en.eq(d_dfi[phy_settings.wrphase].wrdata_en)
|
sd_sys += d_dfi_wrdata_en.eq(d_dfi[self.phy_settings.wrphase].wrdata_en)
|
||||||
|
|
||||||
r_dfi_wrdata_en = Signal(2)
|
r_dfi_wrdata_en = Signal(2)
|
||||||
sd_sdram_half += r_dfi_wrdata_en.eq(Cat(d_dfi_wrdata_en, r_dfi_wrdata_en[0]))
|
sd_sdram_half += r_dfi_wrdata_en.eq(Cat(d_dfi_wrdata_en, r_dfi_wrdata_en[0]))
|
||||||
|
|
||||||
self.comb += drive_dqs.eq(r_dfi_wrdata_en[1])
|
self.comb += drive_dqs.eq(r_dfi_wrdata_en[1])
|
||||||
|
|
||||||
rddata_sr = Signal(read_latency)
|
rddata_sr = Signal(self.phy_settings.read_latency)
|
||||||
sd_sys += rddata_sr.eq(Cat(rddata_sr[1:read_latency], d_dfi[phy_settings.rdphase].rddata_en))
|
sd_sys += rddata_sr.eq(Cat(rddata_sr[1:self.phy_settings.read_latency],
|
||||||
|
d_dfi[self.phy_settings.rdphase].rddata_en))
|
||||||
|
|
||||||
for n, phase in enumerate(self.dfi.phases):
|
for n, phase in enumerate(self.dfi.phases):
|
||||||
self.comb += [
|
self.comb += [
|
||||||
phase.rddata.eq(d_dfi[n].rddata),
|
phase.rddata.eq(d_dfi[n].rddata),
|
||||||
phase.rddata_valid.eq(rddata_sr[0]),
|
phase.rddata_valid.eq(rddata_sr[0]),
|
||||||
]
|
]
|
||||||
|
|
33
top.py
33
top.py
|
@ -25,20 +25,11 @@ def ns(t, margin=True):
|
||||||
t += clk_period_ns/2
|
t += clk_period_ns/2
|
||||||
return ceil(t/clk_period_ns)
|
return ceil(t/clk_period_ns)
|
||||||
|
|
||||||
sdram_phy = lasmicon.PhySettings(
|
|
||||||
type="DDR",
|
|
||||||
dfi_d=64,
|
|
||||||
nphases=2,
|
|
||||||
rdphase=0,
|
|
||||||
wrphase=1,
|
|
||||||
cl=3
|
|
||||||
)
|
|
||||||
sdram_geom = lasmicon.GeomSettings(
|
sdram_geom = lasmicon.GeomSettings(
|
||||||
bank_a=2,
|
bank_a=2,
|
||||||
row_a=13,
|
row_a=13,
|
||||||
col_a=10
|
col_a=10
|
||||||
)
|
)
|
||||||
sdram_phy_read_latency, sdram_phy_write_latency = s6ddrphy.get_latencies(sdram_phy)
|
|
||||||
sdram_timing = lasmicon.TimingSettings(
|
sdram_timing = lasmicon.TimingSettings(
|
||||||
tRP=ns(15),
|
tRP=ns(15),
|
||||||
tRCD=ns(15),
|
tRCD=ns(15),
|
||||||
|
@ -46,9 +37,6 @@ sdram_timing = lasmicon.TimingSettings(
|
||||||
tWTR=2,
|
tWTR=2,
|
||||||
tREFI=ns(7800, False),
|
tREFI=ns(7800, False),
|
||||||
tRFC=ns(70),
|
tRFC=ns(70),
|
||||||
|
|
||||||
read_latency=sdram_phy_read_latency+0,
|
|
||||||
write_latency=sdram_phy_write_latency+0,
|
|
||||||
|
|
||||||
req_queue_size=8,
|
req_queue_size=8,
|
||||||
read_time=32,
|
read_time=32,
|
||||||
|
@ -104,10 +92,20 @@ class SoC(Module):
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, platform, platform_name, with_memtest):
|
def __init__(self, platform, platform_name, with_memtest):
|
||||||
|
#
|
||||||
|
# DFI
|
||||||
|
#
|
||||||
|
self.submodules.ddrphy = s6ddrphy.S6DDRPHY(platform.request("ddram"), memtype="DDR", nphases=2, cl=3, bitslip=0)
|
||||||
|
self.submodules.dfii = dfii.DFIInjector(sdram_geom.mux_a, sdram_geom.bank_a,
|
||||||
|
self.ddrphy.phy_settings.dfi_d, self.ddrphy.phy_settings.nphases)
|
||||||
|
self.submodules.dficon0 = dfi.Interconnect(self.dfii.master, self.ddrphy.dfi)
|
||||||
|
|
||||||
#
|
#
|
||||||
# LASMI
|
# LASMI
|
||||||
#
|
#
|
||||||
self.submodules.lasmicon = lasmicon.LASMIcon(sdram_phy, sdram_geom, sdram_timing)
|
self.submodules.lasmicon = lasmicon.LASMIcon(self.ddrphy.phy_settings, sdram_geom, sdram_timing)
|
||||||
|
self.submodules.dficon1 = dfi.Interconnect(self.lasmicon.dfi, self.dfii.slave)
|
||||||
|
|
||||||
n_lasmims = 7 if with_memtest else 5
|
n_lasmims = 7 if with_memtest else 5
|
||||||
self.submodules.lasmixbar = lasmibus.Crossbar([self.lasmicon.lasmic], n_lasmims, self.lasmicon.nrowbits)
|
self.submodules.lasmixbar = lasmibus.Crossbar([self.lasmicon.lasmic], n_lasmims, self.lasmicon.nrowbits)
|
||||||
lasmims = list(self.lasmixbar.masters)
|
lasmims = list(self.lasmixbar.masters)
|
||||||
|
@ -115,15 +113,6 @@ class SoC(Module):
|
||||||
if with_memtest:
|
if with_memtest:
|
||||||
lasmim_mtw, lasmim_mtr = lasmims.pop(), lasmims.pop()
|
lasmim_mtw, lasmim_mtr = lasmims.pop(), lasmims.pop()
|
||||||
assert(not lasmims)
|
assert(not lasmims)
|
||||||
|
|
||||||
#
|
|
||||||
# DFI
|
|
||||||
#
|
|
||||||
self.submodules.ddrphy = s6ddrphy.S6DDRPHY(platform.request("ddram"), sdram_phy, 0)
|
|
||||||
self.submodules.dfii = dfii.DFIInjector(sdram_geom.mux_a, sdram_geom.bank_a, sdram_phy.dfi_d,
|
|
||||||
sdram_phy.nphases)
|
|
||||||
self.submodules.dficon0 = dfi.Interconnect(self.dfii.master, self.ddrphy.dfi)
|
|
||||||
self.submodules.dficon1 = dfi.Interconnect(self.lasmicon.dfi, self.dfii.slave)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# WISHBONE
|
# WISHBONE
|
||||||
|
|
Loading…
Reference in New Issue