Connect DDR PHY
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Fri, 17 Feb 2012 10:04:44 +0000 (11:04 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Fri, 17 Feb 2012 10:04:44 +0000 (11:04 +0100)
Doesn't do much for the moment, just to check synthesis/P&R.

build.py
constraints.py
milkymist/m1crg/__init__.py
milkymist/s6ddrphy/__init__.py [new file with mode: 0644]
top.py

index fae1a93a04b02e50d1aea1c32071c6985b0f20a9..aaf9c569d2ecfee7bec3b979c81926e0304758f6 100644 (file)
--- a/build.py
+++ b/build.py
@@ -4,13 +4,16 @@ import top
 # list Verilog sources before changing directory
 verilog_sources = []
 def add_core_dir(d):
-       for root, subFolders, files in os.walk(os.path.join("verilog", d)):
-               for f in files:
+       root = os.path.join("verilog", d)
+       files = os.listdir(root)
+       for f in files:
+               if f[-2:] == ".v":
                        verilog_sources.append(os.path.join(root, f))
 def add_core_files(d, files):
        for f in files:
                verilog_sources.append(os.path.join("verilog", d, f))
 add_core_dir("m1crg")
+add_core_dir("s6ddrphy")
 add_core_files("lm32", ["lm32_cpu.v", "lm32_instruction_unit.v", "lm32_decoder.v",
        "lm32_load_store_unit.v", "lm32_adder.v", "lm32_addsub.v", "lm32_logic_op.v",
        "lm32_shifter.v", "lm32_multiplier_spartan6.v", "lm32_mc_arithmetic.v",
@@ -31,7 +34,9 @@ def str2file(filename, contents):
 str2file("soc.v", src_verilog)
 str2file("soc.ucf", src_ucf)
 verilog_sources.append("build/soc.v")
+
 #raise SystemExit
+
 # xst
 xst_prj = ""
 for s in verilog_sources:
index 91b44dd6c4980156cf8172b0a000a91f428f021f..4867ae5bb65e256a057f78eecadad18951e1c2f8 100644 (file)
@@ -1,8 +1,9 @@
-def get(ns, crg0, norflash0, uart0):
+def get(ns, crg0, norflash0, uart0, ddrphy0):
        constraints = []
        def add(signal, pin, vec=-1, iostandard="LVCMOS33", extra=""):
                constraints.append((ns.get_name(signal), vec, pin, iostandard, extra))
        def add_vec(signal, pins, iostandard="LVCMOS33", extra=""):
+               assert(signal.bv.width == len(pins))
                i = 0
                for p in pins:
                        add(signal, p, i, iostandard, extra)
@@ -12,7 +13,7 @@ def get(ns, crg0, norflash0, uart0):
        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")
+       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",
@@ -21,7 +22,7 @@ def get(ns, crg0, norflash0, uart0):
                extra="SLEW = FAST | DRIVE = 8")
        add_vec(norflash0.d, ["AA20", "U14", "U13", "AA6", "AB6", "W4", "Y4", "Y7",
                "AA2", "AB2", "V15", "AA18", "AB18", "Y13", "AA12", "AB12"],
-               extra = "SLEW = FAST | DRIVE = 8 | PULLDOWN")
+               extra="SLEW = FAST | DRIVE = 8 | PULLDOWN")
        add(norflash0.oe_n, "M22", extra="SLEW = FAST | DRIVE = 8")
        add(norflash0.we_n, "N20", extra="SLEW = FAST | DRIVE = 8")
        add(norflash0.ce_n, "M21", extra="SLEW = FAST | DRIVE = 8")
@@ -29,6 +30,24 @@ def get(ns, crg0, norflash0, uart0):
        add(uart0.tx, "L17", extra="SLEW = SLOW")
        add(uart0.rx, "K18", extra="PULLUP")
        
+       ddrsettings = "IOSTANDARD = SSTL2_I"
+       add(ddrphy0.sd_clk_out_p, "M3", extra=ddrsettings)
+       add(ddrphy0.sd_clk_out_n, "L4", extra=ddrsettings)
+       add_vec(ddrphy0.sd_a, ["B1", "B2", "H8", "J7", "E4", "D5", "K7", "F5",
+               "G6", "C1", "C3", "D1", "D2"], extra=ddrsettings)
+       add_vec(ddrphy0.sd_ba, ["A2", "E6"], extra=ddrsettings)
+       add(ddrphy0.sd_cs_n, "F7", extra=ddrsettings)
+       add(ddrphy0.sd_cke, "G7", extra=ddrsettings)
+       add(ddrphy0.sd_ras_n, "E5", extra=ddrsettings)
+       add(ddrphy0.sd_cas_n, "C4", extra=ddrsettings)
+       add(ddrphy0.sd_we_n, "D3", extra=ddrsettings)
+       add_vec(ddrphy0.sd_dq, ["Y2", "W3", "W1", "P8", "P7", "P6", "P5", "T4", "T3",
+               "U4", "V3", "N6", "N7", "M7", "M8", "R4", "P4", "M6", "L6", "P3", "N4",
+               "M5", "V2", "V1", "U3", "U1", "T2", "T1", "R3", "R1", "P2", "P1"],
+               extra=ddrsettings)
+       add_vec(ddrphy0.sd_dm, ["E1", "E3", "F3", "G4"], extra=ddrsettings)
+       add_vec(ddrphy0.sd_dqs, ["F1", "F2", "H5", "H6"], extra=ddrsettings)
+       
        r = ""
        for c in constraints:
                r += "NET \"" + c[0]
@@ -42,6 +61,15 @@ def get(ns, crg0, norflash0, uart0):
        
        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;
+"""
        
        return r
index 38b091210b5c483c5501176f4ccebad41a631acb..37b19e5a01a4e8fc7a806324b11c87f510d3015a 100644 (file)
@@ -23,7 +23,7 @@ class M1CRG:
                        "clk4x_rd_strb_left",
                        "clk4x_rd_right",
                        "clk4x_rd_strb_right"
-                 ]:
+               ]:
                        s = Signal(name=name)
                        setattr(self, name, s)
                        generated.append((name, s))  
diff --git a/milkymist/s6ddrphy/__init__.py b/milkymist/s6ddrphy/__init__.py
new file mode 100644 (file)
index 0000000..3e3b94d
--- /dev/null
@@ -0,0 +1,104 @@
+from migen.fhdl.structure import *
+from migen.bus import dfi
+
+class S6DDRPHY:
+       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",
+                       "reset_n"
+               ]:
+                       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),
+                       ("sd_a", a, outs),
+                       ("sd_ba", ba, outs),
+                       ("sd_cs_n", 1, outs),
+                       ("sd_cke", 1, outs),
+                       ("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)
+                       
+               ]:
+                       s = Signal(BV(width), name=name)
+                       setattr(self, name, s)
+                       l.append((name, s))
+                       self._sd_pins.append(s)
+               
+               self.dfi = dfi.Interface(a, ba, d)
+               ins += self.dfi.get_standard_names(True, False)
+               outs += self.dfi.get_standard_names(False, True)
+               
+               ins += [
+                       ("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",
+                       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)))
+                       ],
+                       clkport="clk")
+
+       def get_fragment(self):
+               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)
+               ]
+               return Fragment(comb, instances=[self._inst], pads=set(self._sd_pins))
diff --git a/top.py b/top.py
index c46eda58a356e716cbc3a8d54ee18d3c78111abe..dbf67dfc58141226d777248f9b2cdeda15d95cdd 100644 (file)
--- a/top.py
+++ b/top.py
@@ -4,7 +4,7 @@ from migen.fhdl.structure import *
 from migen.fhdl import verilog, autofragment
 from migen.bus import wishbone, asmibus, wishbone2asmi, csr, wishbone2csr
 
-from milkymist import m1crg, lm32, norflash, uart, sram#, s6ddrphy
+from milkymist import m1crg, lm32, norflash, uart, sram, s6ddrphy
 import constraints
 
 MHz = 1000000
@@ -12,11 +12,26 @@ clk_freq = (83 + Fraction(1, 3))*MHz
 sram_size = 4096 # in bytes
 l2_size = 8192 # in bytes
 
+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",
+       ]
+       comb = [getattr(phy, name).eq(getattr(crg, name)) for name in names]
+       return Fragment(comb)
+
 def get():
        #
        # ASMI
        #
-       #ddrphy0 = s6ddrphy.S6DDRPHY(13, 2, 128)
+       ddrphy0 = s6ddrphy.S6DDRPHY(13, 2, 128)
        asmihub0 = asmibus.Hub(23, 128, 12) # TODO: get hub from memory controller
        asmiport_wb = asmihub0.get_port()
        asmihub0.finalize()
@@ -67,12 +82,12 @@ def get():
        #
        crg0 = m1crg.M1CRG(50*MHz, clk_freq)
        
-       frag = autofragment.from_local() + interrupts
+       frag = autofragment.from_local() + interrupts + ddrphy_clocking(crg0, ddrphy0)
        src_verilog, vns = verilog.convert(frag,
                {crg0.trigger_reset},
                name="soc",
                clk_signal=crg0.sys_clk,
                rst_signal=crg0.sys_rst,
                return_ns=True)
-       src_ucf = constraints.get(vns, crg0, norflash0, uart0)
+       src_ucf = constraints.get(vns, crg0, norflash0, uart0, ddrphy0)
        return (src_verilog, src_ucf)