From: Florent Kermarrec Date: Tue, 9 Jul 2013 17:41:28 +0000 (+0200) Subject: use Migen s6ddrphy, generate sdram init_sequence in cif.py X-Git-Tag: 24jan2021_ls180~2883 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=60f1585fef12c986b068247ce0cae2e4d9e6db59;p=litex.git use Migen s6ddrphy, generate sdram init_sequence in cif.py --- diff --git a/make.py b/make.py index a69b77d6..040499b6 100755 --- a/make.py +++ b/make.py @@ -27,7 +27,7 @@ TIMESPEC "TSise_sucks1" = FROM "GRPvga_clk" TO "GRPsys_clk" TIG; TIMESPEC "TSise_sucks2" = FROM "GRPsys_clk" TO "GRPvga_clk" TIG; """) - for d in ["mxcrg", "s6ddrphy", "minimac3"]: + for d in ["mxcrg", "minimac3"]: platform.add_source_dir(os.path.join("verilog", d)) platform.add_sources(os.path.join("verilog", "lm32", "submodule", "rtl"), "lm32_cpu.v", "lm32_instruction_unit.v", "lm32_decoder.v", @@ -47,12 +47,16 @@ TIMESPEC "TSise_sucks2" = FROM "GRPsys_clk" TO "GRPvga_clk" TIG; if build_header: csr_header = cif.get_csr_header(soc.csr_base, soc.csrbankarray, soc.interrupt_map) write_to_file("software/include/hw/csr.h", csr_header) + + sdram_phy_header = cif.get_sdram_phy_header(soc.ddrphy) + write_to_file("software/include/hw/sdram_phy.h", sdram_phy_header) + def main(): parser = argparse.ArgumentParser(description="milkymist-ng - a high performance SoC built on Migen technology.") parser.add_argument("-p", "--platform", default="mixxeo", help="platform to build for") parser.add_argument("-B", "--no-bitstream", default=False, action="store_true", help="do not build bitstream file") - parser.add_argument("-H", "--no-header", default=False, action="store_true", help="do not build C header file with CSR/IRQ defs") + parser.add_argument("-H", "--no-header", default=False, action="store_true", help="do not build C header file with CSR/IRQ/SDRAM_PHY defs") parser.add_argument("-l", "--load", default=False, action="store_true", help="load bitstream to SRAM") parser.add_argument("-f", "--flash", default=False, action="store_true", help="load bitstream to flash") args = parser.parse_args() diff --git a/milkymist/cif.py b/milkymist/cif.py index ace7b2af..da253fb8 100644 --- a/milkymist/cif.py +++ b/milkymist/cif.py @@ -1,6 +1,7 @@ from operator import itemgetter import re +from migen.fhdl.std import * from migen.bank.description import CSRStatus def get_macros(filename): @@ -64,3 +65,137 @@ def get_csr_header(csr_base, bank_array, interrupt_map): r += "#define "+name.upper()+"_INTERRUPT "+str(interrupt_nr)+"\n" r += "\n#endif\n" return r + +def get_sdram_phy_header(sdram_phy): + + if sdram_phy.phy_settings.type not in ["SDR", "DDR", "LPDDR", "DDR2"]: + raise NotImplementedError("sdram phy header generator only supports SDR, DDR, LPDDR and DDR2") + + r = "#ifndef __HW_SDRAM_PHY_H\n#define __SDRAM_PHY_H\n" + r += "#include \n#include \n#include \n\n" + + r += "extern 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/* %s */\n" %comment + r += "\tdfii_pi0_address_write(0x%04X);\n" %a + r += "\tdfii_pi0_baddress_write(%d);\n" %ba + if "CKE" in cmd: + r += "\tdfii_control_write(%s);\n" %cmd + else: + r += "\tcommand_p0(%s);\n" %cmd + r += "\tcdelay(%d);\n" %delay + r += "\n" + return r + + + r += "static void init_sequence(void) {\n" + + cl = sdram_phy.phy_settings.cl + + if sdram_phy.phy_settings.type == "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=%d, BL=%d" %(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), + ("Load Mode Register / CL=%d, BL=%d" %(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.type == "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=%d, BL=%d" %(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), + ("Load Mode Register / CL=%d, BL=%d" %(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.type == "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=%d, BL=%d" %(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), + ("Load Mode Register / CL=%d, BL=%d" %(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + elif sdram_phy.phy_settings.type == "DDR2": + 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=%d, BL=%d" %(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), + ("Load Mode Register / CL=%d, BL=%d" %(cl, bl), mr, 0, cmds["MODE_REGISTER"], 200) + ] + + 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/milkymist/lasmicon/__init__.py b/milkymist/lasmicon/__init__.py index f56e9160..f0a38999 100644 --- a/milkymist/lasmicon/__init__.py +++ b/milkymist/lasmicon/__init__.py @@ -7,7 +7,7 @@ from milkymist.lasmicon.refresher import * from milkymist.lasmicon.bankmachine import * from milkymist.lasmicon.multiplexer import * -PhySettings = namedtuple("PhySettings", "dfi_d nphases rdphase wrphase") +PhySettings = namedtuple("PhySettings", "type dfi_d nphases rdphase wrphase cl") class GeomSettings(namedtuple("_GeomSettings", "bank_a row_a col_a")): def __init__(self, *args, **kwargs): diff --git a/milkymist/mxcrg/__init__.py b/milkymist/mxcrg/__init__.py index 7107d023..c487f07a 100644 --- a/milkymist/mxcrg/__init__.py +++ b/milkymist/mxcrg/__init__.py @@ -6,9 +6,9 @@ from migen.bank.description import * class MXCRG(Module, AutoCSR): def __init__(self, pads, outfreq1x): self.clock_domains.cd_sys = ClockDomain() - self.clock_domains.cd_sys2x_270 = ClockDomain() - self.clock_domains.cd_sys4x_wr = ClockDomain() - self.clock_domains.cd_sys4x_rd = ClockDomain() + self.clock_domains.cd_sdram_half = ClockDomain() + self.clock_domains.cd_sdram_full_wr = ClockDomain() + self.clock_domains.cd_sdram_full_rd = ClockDomain() self.clock_domains.cd_eth_rx = ClockDomain() self.clock_domains.cd_eth_tx = ClockDomain() self.clock_domains.cd_vga = ClockDomain(reset_less=True) @@ -44,9 +44,9 @@ class MXCRG(Module, AutoCSR): Instance.Output("sys_clk", self.cd_sys.clk), Instance.Output("sys_rst", self.cd_sys.rst), - Instance.Output("clk2x_270", self.cd_sys2x_270.clk), - Instance.Output("clk4x_wr", self.cd_sys4x_wr.clk), - Instance.Output("clk4x_rd", self.cd_sys4x_rd.clk), + Instance.Output("clk2x_270", self.cd_sdram_half.clk), + Instance.Output("clk4x_wr", self.cd_sdram_full_wr.clk), + Instance.Output("clk4x_rd", self.cd_sdram_full_rd.clk), Instance.Output("eth_rx_clk", self.cd_eth_rx.clk), Instance.Output("eth_tx_clk", self.cd_eth_tx.clk), Instance.Output("vga_clk", self.cd_vga.clk), diff --git a/milkymist/s6ddrphy/__init__.py b/milkymist/s6ddrphy/__init__.py index bd684ce9..c21a3318 100644 --- a/milkymist/s6ddrphy/__init__.py +++ b/milkymist/s6ddrphy/__init__.py @@ -1,40 +1,348 @@ +# +# 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 +# CL + 2 cycles later, along with the assertion +# of dfi_rddata_valid. +# +# This PHY supports configurable CAS Latency. +# Read commands must be sent on phase RDPHASE. +# Write commands must be sent on phase WRPHASE. +#/ + +# Todo: +# - use CSR for bitslip? +# - move sdram clk generation to phy? + from migen.fhdl.std import * -from migen.bus import dfi +from migen.bus.dfi import * +from migen.genlib.record import * class S6DDRPHY(Module): - def __init__(self, pads): - self.dfi = dfi.Interface(flen(pads.a), flen(pads.ba), 2*flen(pads.dq), 2) + def __init__(self, pads, phy_settings, bitslip): + + if phy_settings.type not in ["DDR", "LPDDR", "DDR2"]: + raise NotImplementedError("S6DDRPHY only supports DDR, LPDDR and DDR2") + + a = flen(pads.a) + ba = flen(pads.ba) + d = flen(pads.dq) + nphases = phy_settings.nphases + self.phy_settings = phy_settings + + self.dfi = Interface(a, ba, nphases*d, nphases) self.clk4x_wr_strb = Signal() self.clk4x_rd_strb = Signal() ### - inst_items = [ - Instance.Parameter("NUM_AD", flen(pads.a)), - Instance.Parameter("NUM_BA", flen(pads.ba)), - Instance.Parameter("NUM_D", 2*flen(pads.dq)), - - Instance.Input("sys_clk", ClockSignal()), - Instance.Input("clk2x_270", ClockSignal("sys2x_270")), - Instance.Input("clk4x_wr", ClockSignal("sys4x_wr")), - Instance.Input("clk4x_rd", ClockSignal("sys4x_rd")), - - Instance.Input("clk4x_wr_strb", self.clk4x_wr_strb), - Instance.Input("clk4x_rd_strb", self.clk4x_rd_strb), - - Instance.Output("sd_a", pads.a), - Instance.Output("sd_ba", pads.ba), - Instance.Output("sd_cs_n", pads.cs_n), - Instance.Output("sd_cke", pads.cke), - Instance.Output("sd_ras_n", pads.ras_n), - Instance.Output("sd_cas_n", pads.cas_n), - Instance.Output("sd_we_n", pads.we_n), - Instance.InOut("sd_dq", pads.dq), - Instance.Output("sd_dm", pads.dm), - Instance.InOut("sd_dqs", pads.dqs) + # 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 write clk + sd_sys = getattr(self.sync, "sys") + sd_sdram_half = getattr(self.sync, "sdram_half") + sd_sdram_full_wr = getattr(self.sync, "sdram_full_wr") + sd_sdram_full_rd = getattr(self.sync, "sdram_full_rd") + + 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.cs_n.eq(r_dfi[phase_sel].cs_n), + 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) + ] + + + # + # Bitslip + # + bitslip_cnt = Signal(4) + bitslip_inc = Signal() + + sd_sys +=[ + If(bitslip_cnt==bitslip, + bitslip_inc.eq(0) + ).Else( + bitslip_cnt.eq(bitslip_cnt+1), + bitslip_inc.eq(1) + ) ] - inst_items += [Instance.Input(name, signal) - for name, signal in self.dfi.get_standard_names(True, False)] - inst_items += [Instance.Output(name, signal) - for name, signal in self.dfi.get_standard_names(False, True)] - self.specials += Instance("s6ddrphy", *inst_items) + + # + # DQ/DQS/DM data + # + sdram_half_clk_n = Signal() + self.comb += sdram_half_clk_n.eq(~sdram_half_clk) + + postamble, drive_dqs, dqs_t_d0, dqs_t_d1 = (Signal() for i in range(4)) + + 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", + Instance.Parameter("DDR_ALIGNMENT", "C1"), + Instance.Parameter("INIT", 0), + Instance.Parameter("SRTYPE", "ASYNC"), + + Instance.Input("C0", sdram_half_clk), + Instance.Input("C1", sdram_half_clk_n), + + Instance.Input("CE", 1), + Instance.Input("D0", 0), + Instance.Input("D1", 1), + Instance.Input("R", 0), + Instance.Input("S", 0), + + Instance.Output("Q", dqs_o[i]) + ) + + # DQS tristate cmd + self.specials += Instance("ODDR2", + Instance.Parameter("DDR_ALIGNMENT", "C1"), + Instance.Parameter("INIT", 0), + Instance.Parameter("SRTYPE", "ASYNC"), + + Instance.Input("C0", sdram_half_clk), + Instance.Input("C1", sdram_half_clk_n), + + Instance.Input("CE", 1), + Instance.Input("D0", dqs_t_d0), + Instance.Input("D1", dqs_t_d1), + Instance.Input("R", 0), + Instance.Input("S", 0), + + Instance.Output("Q", dqs_t[i]) + ) + + # DQS tristate buffer + self.specials += Instance("OBUFT", + Instance.Input("I", dqs_o[i]), + Instance.Input("T", dqs_t[i]), + + Instance.Output("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, drive_dq_n, d_drive_dq, d_drive_dq_n = (Signal() for i in range(4)) + self.comb +=[ + drive_dq_n.eq(~drive_dq), + d_drive_dq_n.eq(~d_drive_dq) + ] + + dq_t, dq_o, dq_i = (Signal(d) for i in range(3)) + + for i in range(d): + + # Data serializer + self.specials += Instance("OSERDES2", + Instance.Parameter("DATA_WIDTH", 4), + Instance.Parameter("DATA_RATE_OQ", "SDR"), + Instance.Parameter("DATA_RATE_OT", "SDR"), + Instance.Parameter("SERDES_MODE", "NONE"), + Instance.Parameter("OUTPUT_MODE", "SINGLE_ENDED"), + + Instance.Output("OQ", dq_o[i]), + Instance.Input("OCE", 1), + Instance.Input("CLK0", sdram_full_wr_clk), + Instance.Input("CLK1", 0), + Instance.Input("IOCE", self.clk4x_wr_strb), + Instance.Input("RST", 0), + Instance.Input("CLKDIV", sys_clk), + + Instance.Input("D1", d_dfi[1*nphases+0].wrdata[i]), + Instance.Input("D2", d_dfi[1*nphases+1].wrdata[i+d]), + Instance.Input("D3", d_dfi[1*nphases+1].wrdata[i]), + Instance.Input("D4", d_dfi[0*nphases+0].wrdata[i+d]), + Instance.Output("TQ", dq_t[i]), + Instance.Input("T1", d_drive_dq_n), + Instance.Input("T2", d_drive_dq_n), + Instance.Input("T3", d_drive_dq_n), + Instance.Input("T4", drive_dq_n), + Instance.Input("TRAIN", 0), + Instance.Input("TCE", 1), + Instance.Input("SHIFTIN1", 0), + Instance.Input("SHIFTIN2", 0), + Instance.Input("SHIFTIN3", 0), + Instance.Input("SHIFTIN4", 0), + + Instance.Output("SHIFTOUT1"), + Instance.Output("SHIFTOUT2"), + Instance.Output("SHIFTOUT3"), + Instance.Output("SHIFTOUT4"), + ) + + # Data deserializer + self.specials += Instance("ISERDES2", + Instance.Parameter("DATA_WIDTH", 4), + Instance.Parameter("DATA_RATE", "SDR"), + Instance.Parameter("BITSLIP_ENABLE", "TRUE"), + Instance.Parameter("SERDES_MODE", "NONE"), + Instance.Parameter("INTERFACE_TYPE", "RETIMED"), + + Instance.Input("D", dq_i[i]), + Instance.Input("CE0", 1), + Instance.Input("CLK0", sdram_full_rd_clk), + Instance.Input("CLK1", 0), + Instance.Input("IOCE", self.clk4x_rd_strb), + Instance.Input("RST", ResetSignal()), + Instance.Input("CLKDIV", sys_clk), + Instance.Output("SHIFTIN"), + Instance.Input("BITSLIP", bitslip_inc), + Instance.Output("FABRICOUT"), + + Instance.Output("Q1", d_dfi[0*nphases+0].rddata[i+d]), + Instance.Output("Q2", d_dfi[0*nphases+0].rddata[i]), + Instance.Output("Q3", d_dfi[0*nphases+1].rddata[i+d]), + Instance.Output("Q4", d_dfi[0*nphases+1].rddata[i]), + + Instance.Output("DFB"), + Instance.Output("CFB0"), + Instance.Output("CFB1"), + Instance.Output("VALID"), + Instance.Output("INCDEC"), + Instance.Output("SHIFTOUT") + ) + + # Data buffer + self.specials += Instance("IOBUF", + Instance.Input("I", dq_o[i]), + Instance.Output("O", dq_i[i]), + Instance.Input("T", dq_t[i]), + Instance.InOut("IO", pads.dq[i]) + ) + + for i in range(d//8): + + # Mask serializer + self.specials += Instance("OSERDES2", + Instance.Parameter("DATA_WIDTH", 4), + Instance.Parameter("DATA_RATE_OQ", "SDR"), + Instance.Parameter("DATA_RATE_OT", "SDR"), + Instance.Parameter("SERDES_MODE", "NONE"), + Instance.Parameter("OUTPUT_MODE", "SINGLE_ENDED"), + + Instance.Output("OQ", pads.dm[i]), + Instance.Input("OCE", 1), + Instance.Input("CLK0", sdram_full_wr_clk), + Instance.Input("CLK1", 0), + Instance.Input("IOCE", self.clk4x_wr_strb), + Instance.Input("RST", 0), + Instance.Input("CLKDIV", sys_clk), + + Instance.Input("D1", d_dfi[1*nphases+0].wrdata_mask[i]), + Instance.Input("D2", d_dfi[1*nphases+1].wrdata_mask[i+d//8]), + Instance.Input("D3", d_dfi[1*nphases+1].wrdata_mask[i]), + Instance.Input("D4", d_dfi[0*nphases+0].wrdata_mask[i+d//8]), + Instance.Output("TQ"), + Instance.Input("T1"), + Instance.Input("T2"), + Instance.Input("T3"), + Instance.Input("T4"), + Instance.Input("TRAIN", 0), + Instance.Input("TCE", 0), + Instance.Input("SHIFTIN1", 0), + Instance.Input("SHIFTIN2", 0), + Instance.Input("SHIFTIN3", 0), + Instance.Input("SHIFTIN4", 0), + + Instance.Output("SHIFTOUT1"), + Instance.Output("SHIFTOUT2"), + Instance.Output("SHIFTOUT3"), + Instance.Output("SHIFTOUT4"), + ) + + + # + # DQ/DQS/DM control + # + self.comb += drive_dq.eq(d_dfi[phy_settings.wrphase].wrdata_en) + sd_sys += d_drive_dq.eq(drive_dq) + + + d_dfi_wrdata_en = Signal() + sd_sys += d_dfi_wrdata_en.eq(d_dfi[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(phy_settings.cl+2) + sd_sys += rddata_sr.eq(Cat(rddata_sr[1:phy_settings.cl+2], d_dfi[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/software/bios/sdram.c b/software/bios/sdram.c index 5604cc92..7e8d7f84 100644 --- a/software/bios/sdram.c +++ b/software/bios/sdram.c @@ -2,12 +2,13 @@ #include #include +#include #include #include #include "sdram.h" -static void cdelay(int i) +void cdelay(int i) { while(i > 0) { __asm__ volatile("nop"); @@ -15,59 +16,6 @@ static void cdelay(int i) } } -static void command_p0(int cmd) -{ - dfii_pi0_command_write(cmd); - dfii_pi0_command_issue_write(1); -} - -static void command_p1(int cmd) -{ - dfii_pi1_command_write(cmd); - dfii_pi1_command_issue_write(1); -} - -static void init_sequence(void) -{ - int i; - - /* Bring CKE high */ - dfii_pi0_address_write(0x0000); - dfii_pi0_baddress_write(0); - dfii_control_write(DFII_CONTROL_CKE); - - /* Precharge All */ - dfii_pi0_address_write(0x0400); - command_p0(DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS); - - /* Load Extended Mode Register */ - dfii_pi0_baddress_write(1); - dfii_pi0_address_write(0x0000); - command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); - dfii_pi0_baddress_write(0); - - /* Load Mode Register */ - dfii_pi0_address_write(0x0132); /* Reset DLL, CL=3, BL=4 */ - command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); - cdelay(200); - - /* Precharge All */ - dfii_pi0_address_write(0x0400); - command_p0(DFII_COMMAND_RAS|DFII_COMMAND_WE|DFII_COMMAND_CS); - - /* 2x Auto Refresh */ - for(i=0;i<2;i++) { - dfii_pi0_address_write(0); - command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_CS); - cdelay(4); - } - - /* Load Mode Register */ - dfii_pi0_address_write(0x0032); /* CL=3, BL=4 */ - command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS); - cdelay(200); -} - void ddrsw(void) { dfii_control_write(DFII_CONTROL_CKE); @@ -121,9 +69,9 @@ void ddrrd(char *startaddr) return; } - dfii_pi0_address_write(addr); - dfii_pi0_baddress_write(0); - command_p0(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA); + dfii_pird_address_write(addr); + dfii_pird_baddress_write(0); + command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA); cdelay(15); for(i=0;i<8;i++) @@ -154,9 +102,9 @@ void ddrwr(char *startaddr) MMPTR(0xe000106c+4*i) = 0xf0 + i; } - dfii_pi1_address_write(addr); - dfii_pi1_baddress_write(0); - command_p1(DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS|DFII_COMMAND_WRDATA); + dfii_piwr_address_write(addr); + dfii_piwr_baddress_write(0); + command_pwr(DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS|DFII_COMMAND_WRDATA); } #define TEST_SIZE (4*1024*1024) diff --git a/top.py b/top.py index 4935d26f..b2fb603f 100644 --- a/top.py +++ b/top.py @@ -26,10 +26,12 @@ def ns(t, margin=True): return ceil(t/clk_period_ns) sdram_phy = lasmicon.PhySettings( + type="DDR", dfi_d=64, nphases=2, rdphase=0, - wrphase=1 + wrphase=1, + cl=3 ) sdram_geom = lasmicon.GeomSettings( bank_a=2, @@ -109,7 +111,7 @@ class SoC(Module): # # DFI # - self.submodules.ddrphy = s6ddrphy.S6DDRPHY(platform.request("ddram")) + 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) diff --git a/verilog/s6ddrphy/s6ddrphy.v b/verilog/s6ddrphy/s6ddrphy.v deleted file mode 100644 index 9fb5cdc7..00000000 --- a/verilog/s6ddrphy/s6ddrphy.v +++ /dev/null @@ -1,343 +0,0 @@ -/* - * 1:2 frequency-ratio DDR 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. - */ - -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_270, - 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 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, - inout [NUM_D/2-1:0] sd_dq, - output [NUM_D/16-1:0] sd_dm, - inout [NUM_D/16-1:0] sd_dqs -); - -/* - * Command/address - */ - -reg phase_sel; -always @(posedge clk2x_270) - 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 clk2x_270) 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 - -always @(posedge clk2x_270) begin - if(phase_sel) begin - 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; - end else begin - 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; - end -end - -/* - * DQ/DQS/DM data - */ - -genvar i; - -wire drive_dqs; -wire [NUM_D/16-1:0] dqs_o; -wire [NUM_D/16-1:0] dqs_t; -reg postamble; -generate - for(i=0;i