From: Florent Kermarrec Date: Thu, 17 Apr 2014 17:38:13 +0000 (+0200) Subject: sdramphy: move and clean up s6ddrphy, add generic SDRAM PHY X-Git-Tag: 24jan2021_ls180~2728 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1adceb8276b9e425623f11c436eb388849a25775;p=litex.git sdramphy: move and clean up s6ddrphy, add generic SDRAM PHY --- diff --git a/make.py b/make.py index c245e0c8..c8ef3db3 100755 --- a/make.py +++ b/make.py @@ -7,7 +7,7 @@ from migen.util.misc import autotype from migen.fhdl import simplify from misoclib.gensoc import cpuif -from misoclib.s6ddrphy import initsequence +from misoclib.sdramphy import initsequence import programmer def _get_args(): @@ -151,9 +151,10 @@ Subtarget: {} write_to_file("software/include/generated/mem.h", boilerplate + mem_header) csr_header = cpuif.get_csr_header(soc.csr_base, soc.csrbankarray, soc.interrupt_map) write_to_file("software/include/generated/csr.h", boilerplate + csr_header) - if hasattr(soc, "ddrphy"): - sdram_phy_header = initsequence.get_sdram_phy_header(soc.ddrphy) - write_to_file("software/include/generated/sdram_phy.h", boilerplate + sdram_phy_header) + for sdram_phy in ["sdrphy", "ddrphy"]: + if hasattr(soc, sdram_phy): + sdram_phy_header = initsequence.get_sdram_phy_header(getattr(soc, sdram_phy)) + write_to_file("software/include/generated/sdram_phy.h", boilerplate + sdram_phy_header) if actions["build-csr-csv"]: csr_csv = cpuif.get_csr_csv(soc.csr_base, soc.csrbankarray) diff --git a/misoclib/s6ddrphy/__init__.py b/misoclib/s6ddrphy/__init__.py deleted file mode 100644 index 044c0afc..00000000 --- a/misoclib/s6ddrphy/__init__.py +++ /dev/null @@ -1,367 +0,0 @@ -# 1:2 frequency-ratio DDR / LPDDR / DDR2 PHY 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. -# Read commands must be sent on phase 0. -# Write commands must be sent on phase 1. -# - -# Todo: -# - use CSR for bitslip? -# - add configurable CAS Latency -# - automatically determines wrphase / rdphase / latencies - -from migen.fhdl.std import * -from migen.bus.dfi import * -from migen.genlib.record import * - -from misoclib import lasmicon - -class S6DDRPHY(Module): - def __init__(self, pads, memtype, nphases, cl, rd_bitslip, wr_bitslip, dqs_ddr_alignment): - if memtype not in ["DDR", "LPDDR", "DDR2"]: - raise NotImplementedError("S6DDRPHY only supports DDR, LPDDR and DDR2") - if cl != 3: - raise NotImplementedError("S6DDRPHY only supports CAS LATENCY 3") - a = flen(pads.a) - ba = flen(pads.ba) - d = flen(pads.dq) - - self.phy_settings = lasmicon.PhySettings( - memtype=memtype, - dfi_d=2*d, - nphases=nphases, - rdphase=0, - wrphase=1, - rdcmdphase=1, - wrcmdphase=0, - cl=cl, - read_latency=5, - write_latency=0 - ) - - self.dfi = Interface(a, ba, nphases*d, 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=1) 0 0 - # phase_sel(nphases=2) 0 1 0 1 - # phase_sel(nphases=4) 0 1 2 3 0 1 2 3 - phase_sel = Signal(log2_int(nphases)) - sys_clk_d = Signal() - - sd_sdram_half += [ - If(sys_clk & ~sys_clk_d, - phase_sel.eq(0) - ).Else( - phase_sel.eq(phase_sel+1) - ), - sys_clk_d.eq(sys_clk) - ] - - # register dfi cmds on half_rate clk - r_dfi = Array(Record(phase_cmd_description(a, ba)) for i in range(nphases)) - for n, phase in enumerate(self.dfi.phases): - sd_sdram_half +=[ - 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) - ] - if hasattr(pads, "cs_n"): - sd_sdram_half += pads.cs_n.eq(r_dfi[phase_sel].cs_n) - - # - # 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(d//8) - dqs_t = Signal(d//8) - - self.comb += [ - dqs_t_d0.eq(~(drive_dqs | postamble)), - dqs_t_d1.eq(~drive_dqs), - ] - - for i in range(d//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*d)+phase_rddata_description(nphases*d)) - 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(d) - dq_o = Signal(d) - dq_i = Signal(d) - - dq_wrdata = [] - for i in range(2): - for j in reversed(range(nphases)): - dq_wrdata.append(d_dfi[i*nphases+j].wrdata[:d]) - dq_wrdata.append(d_dfi[i*nphases+j].wrdata[d:]) - - for i in range(d): - # 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+d], - o_Q2=d_dfi[0*nphases+0].rddata[i], - o_Q3=d_dfi[0*nphases+1].rddata[i+d], - 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[:d//8]) - dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[d//8:]) - - for i in range(d//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, - ) - - # - # ODT - # - # ODT not yet supported - if hasattr(pads, "odt"): - self.comb += pads.odt.eq(0) - - # - # DQ/DQS/DM control - # - self.comb += drive_dq.eq(d_dfi[self.phy_settings.wrphase].wrdata_en) - - d_dfi_wrdata_en = Signal() - sd_sys += d_dfi_wrdata_en.eq(d_dfi[self.phy_settings.wrphase].wrdata_en) - - r_dfi_wrdata_en = Signal(2) - 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]) - - rddata_sr = Signal(self.phy_settings.read_latency) - 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): - self.comb += [ - phase.rddata.eq(d_dfi[n].rddata), - phase.rddata_valid.eq(rddata_sr[0]), - ] diff --git a/misoclib/s6ddrphy/initsequence.py b/misoclib/s6ddrphy/initsequence.py deleted file mode 100644 index cf76d4e9..00000000 --- a/misoclib/s6ddrphy/initsequence.py +++ /dev/null @@ -1,146 +0,0 @@ -from migen.fhdl.std import log2_int - -def get_sdram_phy_header(sdram_phy): - 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") - - r = "#ifndef __GENERATED_SDRAM_PHY_H\n#define __GENERATED_SDRAM_PHY_H\n" - r += "#include \n#include \n#include \n\n" - - r += "static void cdelay(int i);\n" - - # - # commands_px functions - # - for n in range(sdram_phy.phy_settings.nphases): - r += """ -static void command_p{n}(int cmd) -{{ - dfii_pi{n}_command_write(cmd); - dfii_pi{n}_command_issue_write(1); -}}""".format(n=str(n)) - r += "\n\n" - - # - # rd/wr access macros - # - r += """ -#define dfii_pird_address_write(X) dfii_pi{rdphase}_address_write(X) -#define dfii_piwr_address_write(X) dfii_pi{wrphase}_address_write(X) - -#define dfii_pird_baddress_write(X) dfii_pi{rdphase}_baddress_write(X) -#define dfii_piwr_baddress_write(X) dfii_pi{wrphase}_baddress_write(X) - -#define command_prd(X) command_p{rdphase}(X) -#define command_pwr(X) command_p{wrphase}(X) -""".format(rdphase=str(sdram_phy.phy_settings.rdphase), wrphase=str(sdram_phy.phy_settings.wrphase)) - r +="\n" - - # - # init sequence - # - cmds = { - "PRECHARGE_ALL" : "DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS", - "MODE_REGISTER" : "DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS", - "AUTO_REFRESH" : "DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_CS", - "CKE" : "DFII_CONTROL_CKE" - } - - def gen_cmd(comment, a, ba, cmd, delay): - r = "\t/* {0} */\n".format(comment) - r += "\tdfii_pi0_address_write({0:#x});\n".format(a) - r += "\tdfii_pi0_baddress_write({0:d});\n".format(ba) - if "CKE" in cmd: - r += "\tdfii_control_write({0});\n".format(cmd) - else: - r += "\tcommand_p0({0});\n".format(cmd) - r += "\tcdelay({0:d});\n".format(delay) - r += "\n" - return r - - - r += "static void init_sequence(void)\n{\n" - - cl = sdram_phy.phy_settings.cl - - if sdram_phy.phy_settings.memtype == "SDR": - bl = 1*sdram_phy.phy_settings.nphases - mr = log2_int(bl) + (cl << 4) - reset_dll = 1 << 8 - - init_sequence = [ - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 2000), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) - ] - - elif sdram_phy.phy_settings.memtype == "DDR": - bl = 2*sdram_phy.phy_settings.nphases - mr = log2_int(bl) + (cl << 4) - emr = 0 - reset_dll = 1 << 8 - - init_sequence = [ - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 2000), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Load Extended Mode Register", emr, 1, cmds["MODE_REGISTER"], 0), - ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) - ] - - elif sdram_phy.phy_settings.memtype == "LPDDR": - bl = 2*sdram_phy.phy_settings.nphases - mr = log2_int(bl) + (cl << 4) - emr = 0 - reset_dll = 1 << 8 - - init_sequence = [ - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 2000), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Load Extended Mode Register", emr, 2, cmds["MODE_REGISTER"], 0), - ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) - ] - - elif sdram_phy.phy_settings.memtype == "DDR2": - bl = 2*sdram_phy.phy_settings.nphases - wr = 2 - mr = log2_int(bl) + (cl << 4) + (wr << 9) - emr = 0 - emr2 = 0 - emr3 = 0 - reset_dll = 1 << 8 - ocd = 7 << 7 - - init_sequence = [ - ("Bring CKE high", 0x0000, 0, cmds["CKE"], 2000), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Load Extended Mode Register 3", emr3, 3, cmds["MODE_REGISTER"], 0), - ("Load Extended Mode Register 2", emr2, 2, cmds["MODE_REGISTER"], 0), - ("Load Extended Mode Register", emr, 1, cmds["MODE_REGISTER"], 0), - ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), - ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), - ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200), - ("Load Extended Mode Register / OCD Default", emr+ocd, 1, cmds["MODE_REGISTER"], 0), - ("Load Extended Mode Register / OCD Exit", emr, 1, cmds["MODE_REGISTER"], 0), - ] - - for comment, a, ba, cmd, delay in init_sequence: - r += gen_cmd(comment, a, ba, cmd, delay) - - r += "}\n" - r += "#endif\n" - - return r diff --git a/misoclib/sdramphy/gensdrphy.py b/misoclib/sdramphy/gensdrphy.py new file mode 100644 index 00000000..01ccc195 --- /dev/null +++ b/misoclib/sdramphy/gensdrphy.py @@ -0,0 +1,97 @@ +# +# 1:1 frequency-ratio Generic SDR PHY +# +# The GENSDRPHY is validated on CycloneIV (Altera) but since it does +# not use vendor-dependent code, it can also be used on other architectures. +# +# The PHY needs 2 Clock domains: +# - sys_clk : The System Clock domain +# - sys_clk_ps : The System Clock domain with its phase shifted +# (-0.75ns on C4@100MHz) +# +# 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 +# 4 cycles later, along with the assertion of +# dfi_rddata_valid. +# +# This PHY only supports CAS Latency 2. +# + +from migen.fhdl.std import * +from migen.bus.dfi import * +from migen.genlib.record import * +from migen.fhdl.specials import * + +from misoclib import lasmicon + +class GENSDRPHY(Module): + def __init__(self, pads, memtype, nphases, cl): + if memtype not in ["SDR"]: + raise NotImplementedError("GENSDRPHY only supports SDR") + if cl != 2: + raise NotImplementedError("GENSDRPHY only supports CAS LATENCY 2") + if nphases > 1: + raise NotImplementedError("GENSDRPHY only supports Full Rate (nphases=1)") + + a = flen(pads.a) + ba = flen(pads.ba) + d = flen(pads.dq) + + self.phy_settings = lasmicon.PhySettings( + memtype=memtype, + dfi_d=d, + nphases=nphases, + rdphase=0, + wrphase=0, + rdcmdphase=0, + wrcmdphase=0, + cl=cl, + read_latency=4, + write_latency=0 + ) + + self.dfi = Interface(a, ba, nphases*d, nphases) + + ### + + # + # Command/address + # + self.sync += [ + pads.a.eq(self.dfi.p0.address), + pads.ba.eq(self.dfi.p0.bank), + pads.cs_n.eq(self.dfi.p0.cs_n), + pads.cke.eq(self.dfi.p0.cke), + pads.cas_n.eq(self.dfi.p0.cas_n), + pads.ras_n.eq(self.dfi.p0.ras_n), + pads.we_n.eq(self.dfi.p0.we_n) + ] + if hasattr(pads, "cs_n"): + self.sync += pads.cs_n.eq(self.dfi.p0.cs_n), + + # + # DQ/DQS/DM data + # + sd_dq_out = Signal(d) + drive_dq = Signal() + self.sync += sd_dq_out.eq(self.dfi.p0.wrdata), + self.specials += Tristate(pads.dq, sd_dq_out, drive_dq) + self.comb += pads.dqm.eq(0) + sd_dq_in_ps = Signal(d) + self.sync.sys_ps += sd_dq_in_ps.eq(pads.dq) + self.sync += self.dfi.p0.rddata.eq(sd_dq_in_ps) + + # + # DQ/DM control + # + d_dfi_wrdata_en = Signal() + self.sync += d_dfi_wrdata_en.eq(self.dfi.p0.wrdata_en) + self.comb += drive_dq.eq(d_dfi_wrdata_en) + + rddata_sr = Signal(4) + self.comb += self.dfi.p0.rddata_valid.eq(rddata_sr[0]) + self.sync += rddata_sr.eq(Cat(self.dfi.p0.rddata_en, rddata_sr[1:])) diff --git a/misoclib/sdramphy/initsequence.py b/misoclib/sdramphy/initsequence.py new file mode 100644 index 00000000..cf76d4e9 --- /dev/null +++ b/misoclib/sdramphy/initsequence.py @@ -0,0 +1,146 @@ +from migen.fhdl.std import log2_int + +def get_sdram_phy_header(sdram_phy): + 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") + + r = "#ifndef __GENERATED_SDRAM_PHY_H\n#define __GENERATED_SDRAM_PHY_H\n" + r += "#include \n#include \n#include \n\n" + + r += "static void cdelay(int i);\n" + + # + # commands_px functions + # + for n in range(sdram_phy.phy_settings.nphases): + r += """ +static void command_p{n}(int cmd) +{{ + dfii_pi{n}_command_write(cmd); + dfii_pi{n}_command_issue_write(1); +}}""".format(n=str(n)) + r += "\n\n" + + # + # rd/wr access macros + # + r += """ +#define dfii_pird_address_write(X) dfii_pi{rdphase}_address_write(X) +#define dfii_piwr_address_write(X) dfii_pi{wrphase}_address_write(X) + +#define dfii_pird_baddress_write(X) dfii_pi{rdphase}_baddress_write(X) +#define dfii_piwr_baddress_write(X) dfii_pi{wrphase}_baddress_write(X) + +#define command_prd(X) command_p{rdphase}(X) +#define command_pwr(X) command_p{wrphase}(X) +""".format(rdphase=str(sdram_phy.phy_settings.rdphase), wrphase=str(sdram_phy.phy_settings.wrphase)) + r +="\n" + + # + # init sequence + # + cmds = { + "PRECHARGE_ALL" : "DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS", + "MODE_REGISTER" : "DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS", + "AUTO_REFRESH" : "DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_CS", + "CKE" : "DFII_CONTROL_CKE" + } + + def gen_cmd(comment, a, ba, cmd, delay): + r = "\t/* {0} */\n".format(comment) + r += "\tdfii_pi0_address_write({0:#x});\n".format(a) + r += "\tdfii_pi0_baddress_write({0:d});\n".format(ba) + if "CKE" in cmd: + r += "\tdfii_control_write({0});\n".format(cmd) + else: + r += "\tcommand_p0({0});\n".format(cmd) + r += "\tcdelay({0:d});\n".format(delay) + r += "\n" + return r + + + r += "static void init_sequence(void)\n{\n" + + cl = sdram_phy.phy_settings.cl + + if sdram_phy.phy_settings.memtype == "SDR": + bl = 1*sdram_phy.phy_settings.nphases + mr = log2_int(bl) + (cl << 4) + reset_dll = 1 << 8 + + init_sequence = [ + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 2000), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.memtype == "DDR": + bl = 2*sdram_phy.phy_settings.nphases + mr = log2_int(bl) + (cl << 4) + emr = 0 + reset_dll = 1 << 8 + + init_sequence = [ + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 2000), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Load Extended Mode Register", emr, 1, cmds["MODE_REGISTER"], 0), + ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.memtype == "LPDDR": + bl = 2*sdram_phy.phy_settings.nphases + mr = log2_int(bl) + (cl << 4) + emr = 0 + reset_dll = 1 << 8 + + init_sequence = [ + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 2000), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Load Extended Mode Register", emr, 2, cmds["MODE_REGISTER"], 0), + ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.memtype == "DDR2": + bl = 2*sdram_phy.phy_settings.nphases + wr = 2 + mr = log2_int(bl) + (cl << 4) + (wr << 9) + emr = 0 + emr2 = 0 + emr3 = 0 + reset_dll = 1 << 8 + ocd = 7 << 7 + + init_sequence = [ + ("Bring CKE high", 0x0000, 0, cmds["CKE"], 2000), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Load Extended Mode Register 3", emr3, 3, cmds["MODE_REGISTER"], 0), + ("Load Extended Mode Register 2", emr2, 2, cmds["MODE_REGISTER"], 0), + ("Load Extended Mode Register", emr, 1, cmds["MODE_REGISTER"], 0), + ("Load Mode Register / Reset DLL, CL={0:d}, BL={1:d}".format(cl, bl), mr + reset_dll, 0, cmds["MODE_REGISTER"], 200), + ("Precharge All", 0x0400, 0, cmds["PRECHARGE_ALL"], 0), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Auto Refresh", 0x0, 0, cmds["AUTO_REFRESH"], 4), + ("Load Mode Register / CL={0:d}, BL={1:d}".format(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200), + ("Load Extended Mode Register / OCD Default", emr+ocd, 1, cmds["MODE_REGISTER"], 0), + ("Load Extended Mode Register / OCD Exit", emr, 1, cmds["MODE_REGISTER"], 0), + ] + + for comment, a, ba, cmd, delay in init_sequence: + r += gen_cmd(comment, a, ba, cmd, delay) + + r += "}\n" + r += "#endif\n" + + return r diff --git a/misoclib/sdramphy/s6ddrphy.py b/misoclib/sdramphy/s6ddrphy.py new file mode 100644 index 00000000..7211ca00 --- /dev/null +++ b/misoclib/sdramphy/s6ddrphy.py @@ -0,0 +1,361 @@ +# 1:2 frequency-ratio DDR / LPDDR / DDR2 PHY 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. +# Read commands must be sent on phase 0. +# Write commands must be sent on phase 1. +# + +from migen.fhdl.std import * +from migen.bus.dfi import * +from migen.genlib.record import * + +from misoclib import lasmicon + +class S6DDRPHY(Module): + def __init__(self, pads, memtype, nphases, cl, rd_bitslip, wr_bitslip, dqs_ddr_alignment): + if memtype not in ["DDR", "LPDDR", "DDR2"]: + raise NotImplementedError("S6DDRPHY only supports DDR, LPDDR and DDR2") + if cl != 3: + raise NotImplementedError("S6DDRPHY only supports CAS LATENCY 3") + if nphases != 2: + raise NotImplementedError("S6DDRPHY only supports Half Rate (nphases=2)") + a = flen(pads.a) + ba = flen(pads.ba) + d = flen(pads.dq) + + self.phy_settings = lasmicon.PhySettings( + memtype=memtype, + dfi_d=2*d, + nphases=nphases, + rdphase=0, + wrphase=1, + rdcmdphase=1, + wrcmdphase=0, + cl=cl, + read_latency=5, + write_latency=0 + ) + + self.dfi = Interface(a, ba, nphases*d, 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)) + sys_clk_d = Signal() + + sd_sdram_half += [ + If(sys_clk & ~sys_clk_d, + phase_sel.eq(0) + ).Else( + phase_sel.eq(phase_sel+1) + ), + sys_clk_d.eq(sys_clk) + ] + + # register dfi cmds on half_rate clk + r_dfi = Array(Record(phase_cmd_description(a, ba)) for i in range(nphases)) + for n, phase in enumerate(self.dfi.phases): + sd_sdram_half +=[ + 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) + ] + if hasattr(pads, "cs_n"): + sd_sdram_half += pads.cs_n.eq(r_dfi[phase_sel].cs_n) + + # + # 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(d//8) + dqs_t = Signal(d//8) + + self.comb += [ + dqs_t_d0.eq(~(drive_dqs | postamble)), + dqs_t_d1.eq(~drive_dqs), + ] + + for i in range(d//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*d)+phase_rddata_description(nphases*d)) + 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(d) + dq_o = Signal(d) + dq_i = Signal(d) + + dq_wrdata = [] + for i in range(2): + for j in reversed(range(nphases)): + dq_wrdata.append(d_dfi[i*nphases+j].wrdata[:d]) + dq_wrdata.append(d_dfi[i*nphases+j].wrdata[d:]) + + for i in range(d): + # 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+d], + o_Q2=d_dfi[0*nphases+0].rddata[i], + o_Q3=d_dfi[0*nphases+1].rddata[i+d], + 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[:d//8]) + dq_wrdata_mask.append(d_dfi[i*nphases+j].wrdata_mask[d//8:]) + + for i in range(d//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, + ) + + # + # ODT + # + # ODT not yet supported + if hasattr(pads, "odt"): + self.comb += pads.odt.eq(0) + + # + # DQ/DQS/DM control + # + self.comb += drive_dq.eq(d_dfi[self.phy_settings.wrphase].wrdata_en) + + d_dfi_wrdata_en = Signal() + sd_sys += d_dfi_wrdata_en.eq(d_dfi[self.phy_settings.wrphase].wrdata_en) + + r_dfi_wrdata_en = Signal(2) + 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]) + + rddata_sr = Signal(self.phy_settings.read_latency) + 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): + self.comb += [ + phase.rddata.eq(d_dfi[n].rddata), + phase.rddata_valid.eq(rddata_sr[0]), + ] diff --git a/targets/mlabs_video.py b/targets/mlabs_video.py index 06b9f4c9..3c179c26 100644 --- a/targets/mlabs_video.py +++ b/targets/mlabs_video.py @@ -4,7 +4,8 @@ from fractions import Fraction from migen.fhdl.std import * from mibuild.generic_platform import ConstraintError -from misoclib import lasmicon, mxcrg, norflash16, s6ddrphy, minimac3, framebuffer, dvisampler, gpio +from misoclib import lasmicon, mxcrg, norflash16, minimac3, framebuffer, dvisampler, gpio +from misoclib.sdramphy import s6ddrphy from misoclib.gensoc import SDRAMSoC class _MXClockPads: