Generate all clocks for the DDR PHY

This commit is contained in:
Sebastien Bourdeauducq 2012-02-16 18:02:37 +01:00
parent 859c9d8849
commit 72f9af9d90
7 changed files with 495 additions and 100 deletions

View File

@ -10,7 +10,7 @@ def add_core_dir(d):
def add_core_files(d, files): def add_core_files(d, files):
for f in files: for f in files:
verilog_sources.append(os.path.join("verilog", d, f)) verilog_sources.append(os.path.join("verilog", d, f))
add_core_dir("m1reset") add_core_dir("m1crg")
add_core_files("lm32", ["lm32_cpu.v", "lm32_instruction_unit.v", "lm32_decoder.v", 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_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", "lm32_shifter.v", "lm32_multiplier_spartan6.v", "lm32_mc_arithmetic.v",
@ -59,4 +59,4 @@ os.system("map -ol high -w soc.ngd")
os.system("par -ol high -w soc.ncd soc-routed.ncd") os.system("par -ol high -w soc.ncd soc-routed.ncd")
# bitgen # bitgen
os.system("bitgen -g LCK_cycle:6 -g Binary:Yes -g INIT_9K:Yes -w soc-routed.ncd soc.bit") os.system("bitgen -g Binary:Yes -g INIT_9K:Yes -w soc-routed.ncd soc.bit")

View File

@ -1,4 +1,4 @@
def get(ns, clkfx_sys, reset0, norflash0, uart0): def get(ns, crg0, norflash0, uart0):
constraints = [] constraints = []
def add(signal, pin, vec=-1, iostandard="LVCMOS33", extra=""): def add(signal, pin, vec=-1, iostandard="LVCMOS33", extra=""):
constraints.append((ns.get_name(signal), vec, pin, iostandard, extra)) constraints.append((ns.get_name(signal), vec, pin, iostandard, extra))
@ -8,12 +8,12 @@ def get(ns, clkfx_sys, reset0, norflash0, uart0):
add(signal, p, i, iostandard, extra) add(signal, p, i, iostandard, extra)
i += 1 i += 1
add(clkfx_sys.clkin, "AB11", extra="TNM_NET = \"GRPclk50\"") add(crg0.clkin, "AB11", extra="TNM_NET = \"GRPclk50\"")
add(crg0.ac97_rst_n, "D6")
add(reset0.trigger_reset, "AA4") add(crg0.videoin_rst_n, "W17")
add(reset0.ac97_rst_n, "D6") add(crg0.flash_rst_n, "P22", extra="SLEW = FAST | DRIVE = 8")
add(reset0.videoin_rst_n, "W17") add(crg0.rd_clk_lb, "K5")
add(reset0.flash_rst_n, "P22", extra="SLEW = FAST | DRIVE = 8") add(crg0.trigger_reset, "AA4")
add_vec(norflash0.adr, ["L22", "L20", "K22", "K21", "J19", "H20", "F22", add_vec(norflash0.adr, ["L22", "L20", "K22", "K21", "J19", "H20", "F22",
"F21", "K17", "J17", "E22", "E20", "H18", "H19", "F20", "F21", "K17", "J17", "E22", "E20", "H18", "H19", "F20",

View File

@ -0,0 +1,51 @@
from fractions import Fraction
from migen.fhdl.structure import *
class M1CRG:
def __init__(self, infreq, outfreq1x):
self.clkin = Signal()
self.trigger_reset = Signal()
generated = []
for name in [
"sys_clk",
"sys_rst",
"ac97_rst_n",
"videoin_rst_n",
"flash_rst_n",
"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"
]:
s = Signal(name=name)
setattr(self, name, s)
generated.append((name, s))
self.rd_clk_lb = Signal()
ratio = Fraction(outfreq1x)/Fraction(infreq)
in_period = float(Fraction(1000000000)/Fraction(infreq))
self._inst = Instance("m1crg",
generated,
[
("clkin", self.clkin),
("trigger_reset", self.trigger_reset),
("rd_clk_lb", self.rd_clk_lb) # TODO: inout
], [
("in_period", in_period),
("f_mult", ratio.numerator),
("f_div", ratio.denominator)
]
)
def get_fragment(self):
return Fragment(instances=[self._inst],
pads={self.clkin, self.ac97_rst_n, self.videoin_rst_n, self.flash_rst_n, self.rd_clk_lb})

View File

@ -1,20 +0,0 @@
from migen.fhdl.structure import *
class M1Reset:
def __init__(self):
self.trigger_reset = Signal()
self.sys_rst = Signal()
self.ac97_rst_n = Signal()
self.videoin_rst_n = Signal()
self.flash_rst_n = Signal()
self._inst = Instance("m1reset",
[("sys_rst", self.sys_rst),
("ac97_rst_n", self.ac97_rst_n),
("videoin_rst_n", self.videoin_rst_n),
("flash_rst_n", self.flash_rst_n)],
[("trigger_reset", self.trigger_reset)],
clkport="sys_clk")
def get_fragment(self):
return Fragment(instances=[self._inst],
pads={self.ac97_rst_n, self.videoin_rst_n, self.flash_rst_n})

20
top.py
View File

@ -1,12 +1,14 @@
from fractions import Fraction
from migen.fhdl.structure import * from migen.fhdl.structure import *
from migen.fhdl import verilog, autofragment from migen.fhdl import verilog, autofragment
from migen.bus import wishbone, asmibus, wishbone2asmi, csr, wishbone2csr from migen.bus import wishbone, asmibus, wishbone2asmi, csr, wishbone2csr
from milkymist import m1reset, clkfx, lm32, norflash, uart, sram from milkymist import m1crg, lm32, norflash, uart, sram#, s6ddrphy
import constraints import constraints
MHz = 1000000 MHz = 1000000
clk_freq = 80*MHz clk_freq = (83 + Fraction(1, 3))*MHz
sram_size = 4096 # in bytes sram_size = 4096 # in bytes
l2_size = 8192 # in bytes l2_size = 8192 # in bytes
@ -14,7 +16,8 @@ def get():
# #
# ASMI # ASMI
# #
asmihub0 = asmibus.Hub(24, 64, 8) # TODO: get hub from memory controller #ddrphy0 = s6ddrphy.S6DDRPHY(13, 2, 128)
asmihub0 = asmibus.Hub(23, 128, 12) # TODO: get hub from memory controller
asmiport_wb = asmihub0.get_port() asmiport_wb = asmihub0.get_port()
asmihub0.finalize() asmihub0.finalize()
@ -62,15 +65,14 @@ def get():
# #
# Housekeeping # Housekeeping
# #
clkfx_sys = clkfx.ClkFX(50*MHz, clk_freq) crg0 = m1crg.M1CRG(50*MHz, clk_freq)
reset0 = m1reset.M1Reset()
frag = autofragment.from_local() + interrupts frag = autofragment.from_local() + interrupts
src_verilog, vns = verilog.convert(frag, src_verilog, vns = verilog.convert(frag,
{clkfx_sys.clkin, reset0.trigger_reset}, {crg0.trigger_reset},
name="soc", name="soc",
clk_signal=clkfx_sys.clkout, clk_signal=crg0.sys_clk,
rst_signal=reset0.sys_rst, rst_signal=crg0.sys_rst,
return_ns=True) return_ns=True)
src_ucf = constraints.get(vns, clkfx_sys, reset0, norflash0, uart0) src_ucf = constraints.get(vns, crg0, norflash0, uart0)
return (src_verilog, src_ucf) return (src_verilog, src_ucf)

424
verilog/m1crg/m1crg.v Normal file
View File

@ -0,0 +1,424 @@
/*
* Milkymist-NG SoC
* Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module m1crg #(
parameter in_period = 0.0,
parameter f_mult = 0,
parameter f_div = 0,
parameter clk2x_period = (in_period*f_div)/(2.0*f_mult)
) (
input clkin,
input trigger_reset,
output sys_clk,
output reg sys_rst,
/* Reset off-chip devices */
output ac97_rst_n,
output videoin_rst_n,
output flash_rst_n,
/* DDR PHY clocks and reset */
output clk2x_90,
output clk4x_wr_left,
output clk4x_wr_strb_left,
output clk4x_wr_right,
output clk4x_wr_strb_right,
output clk4x_rd_left,
output clk4x_rd_strb_left,
output clk4x_rd_right,
output clk4x_rd_strb_right,
inout rd_clk_lb /* < unconnected clock pin for read clock PLL loopback */
);
/*
* Reset
*/
wire reset_n;
reg [19:0] rst_debounce;
always @(posedge sys_clk, negedge reset_n) begin
if(~reset_n) begin
rst_debounce <= 20'hFFFFF;
sys_rst <= 1'b1;
end else begin
if(trigger_reset)
rst_debounce <= 20'hFFFFF;
else if(rst_debounce != 20'd0)
rst_debounce <= rst_debounce - 20'd1;
sys_rst <= rst_debounce != 20'd0;
end
end
assign ac97_rst_n = ~sys_rst;
assign videoin_rst_n = ~sys_rst;
/*
* We must release the Flash reset before the system reset
* because the Flash needs some time to come out of reset
* and the CPU begins fetching instructions from it
* as soon as the system reset is released.
* From datasheet, minimum reset pulse width is 100ns
* and reset-to-read time is 150ns.
*/
reg [7:0] flash_rstcounter;
always @(posedge sys_clk, negedge reset_n) begin
if(~reset_n) begin
flash_rstcounter <= 8'd0;
end else begin
if(trigger_reset)
flash_rstcounter <= 8'd0;
else if(~flash_rstcounter[7])
flash_rstcounter <= flash_rstcounter + 8'd1;
end
end
assign flash_rst_n = flash_rstcounter[7];
/*
* Clock management. Largely taken from the NWL reference design.
*/
wire sdr_clkin;
wire clkdiv;
IBUF #(
.IOSTANDARD("DEFAULT")
) clk2_iob (
.I(clkin),
.O(sdr_clkin)
);
BUFIO2 #(
.DIVIDE(1),
.DIVIDE_BYPASS("FALSE"),
.I_INVERT("FALSE")
) bufio2_inst2 (
.I(sdr_clkin),
.IOCLK(),
.DIVCLK(clkdiv),
.SERDESSTROBE()
);
wire pll1_lckd;
wire buf_pll1_fb_out;
wire pll1out0;
wire pll1out1;
wire pll1out2;
wire pll1out3;
PLL_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT(4*f_mult),
.CLKFBOUT_PHASE(0.0),
.CLKIN1_PERIOD(in_period),
.CLKIN2_PERIOD(in_period),
.CLKOUT0_DIVIDE(f_div),
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0),
.CLKOUT1_DIVIDE(f_div),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT1_PHASE(0),
.CLKOUT2_DIVIDE(4*f_div),
.CLKOUT2_DUTY_CYCLE(0.5),
.CLKOUT2_PHASE(0.0),
.CLKOUT3_DIVIDE(2*f_div),
.CLKOUT3_DUTY_CYCLE(0.5),
.CLKOUT3_PHASE(90),
.CLKOUT4_DIVIDE(7),
.CLKOUT4_DUTY_CYCLE(0.5),
.CLKOUT4_PHASE(0),
.CLKOUT5_DIVIDE(7),
.CLKOUT5_DUTY_CYCLE(0.5),
.CLKOUT5_PHASE(0.0),
.COMPENSATION("INTERNAL"),
.DIVCLK_DIVIDE(1),
.REF_JITTER(0.100),
.CLK_FEEDBACK("CLKFBOUT"),
.SIM_DEVICE("SPARTAN6")
) pll1 (
.CLKFBDCM(),
.CLKFBOUT(buf_pll1_fb_out),
.CLKOUT0(pll1out0), /* < x4 180 clock for transmitter */
.CLKOUT1(pll1out1), /* < x4 180 clock for transmitter */
.CLKOUT2(pll1out2), /* < x1 clock for memory controller */
.CLKOUT3(pll1out3), /* < x2 90 clock to generate memory clock, clock DQS and memory address and control signals. */
.CLKOUT4(),
.CLKOUT5(),
.CLKOUTDCM0(),
.CLKOUTDCM1(),
.CLKOUTDCM2(),
.CLKOUTDCM3(),
.CLKOUTDCM4(),
.CLKOUTDCM5(),
.DO(),
.DRDY(),
.LOCKED(pll1_lckd),
.CLKFBIN(buf_pll1_fb_out),
.CLKIN1(clkdiv),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.DADDR(5'b00000),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'h0000),
.DWE(1'b0),
.RST(1'b0),
.REL(1'b0)
);
BUFPLL #(
.DIVIDE(4)
) wr_bufpll_left (
.PLLIN(pll1out0),
.GCLK(sys_clk),
.LOCKED(pll1_lckd),
.IOCLK(clk4x_wr_left),
.LOCK(),
.SERDESSTROBE(clk4x_wr_strb_left)
);
BUFPLL #(
.DIVIDE(4)
) wr_bufpll_right (
.PLLIN(pll1out1),
.GCLK(sys_clk),
.LOCKED(pll1_lckd),
.IOCLK(clk4x_wr_right),
.LOCK(),
.SERDESSTROBE(clk4x_wr_strb_right)
);
BUFG bufg_x1(
.I(pll1out2),
.O(sys_clk)
);
BUFG bufg_x2_2(
.I(pll1out3),
.O(clk2x_90)
);
/*
* Generate clk4x_rd. This clock is sourced from clk2x_90.
* An IODELAY2 element is included in the path of this clock so that
* any variation in IDELAY element's base delay is compensated when this clock
* is used to capture read data which also goes through IDELAY element.
*/
wire rd_clk_out;
ODDR2 #(
.DDR_ALIGNMENT("C0"),
.INIT(1'b0),
.SRTYPE("ASYNC")
) rd_clk_out_inst (
.Q(rd_clk_out),
.C0(clk2x_90),
.C1(~clk2x_90),
.CE(1'b1),
.D0(1'b1),
.D1(1'b0),
.R(1'b0),
.S(1'b0)
);
wire rd_clk_out_oe_n;
ODDR2 #(
.DDR_ALIGNMENT("C0"),
.INIT(1'b0),
.SRTYPE("ASYNC")
) rd_clk_out_oe_inst (
.Q(rd_clk_out_oe_n),
.C0(clk2x_90),
.C1(~clk2x_90),
.CE(1'b1),
.D0(1'b0),
.D1(1'b0),
.R(1'b0),
.S(1'b0)
);
wire rd_clk_fb;
/* Dummy pin used for calibration */
IOBUF rd_clk_loop_back_inst(
.O(rd_clk_fb),
.IO(rd_clk_lb),
.I(rd_clk_out),
.T(rd_clk_out_oe_n)
);
wire rd_clk_fb_dly;
IODELAY2 #(
.DATA_RATE("DDR"),
.IDELAY_VALUE(0),
.IDELAY2_VALUE(0),
.IDELAY_MODE("NORMAL"),
.ODELAY_VALUE(0),
.IDELAY_TYPE("FIXED"),
.COUNTER_WRAPAROUND("STAY_AT_LIMIT"),
.DELAY_SRC("IDATAIN"),
.SERDES_MODE("NONE"),
.SIM_TAPDELAY_VALUE(49)
) iodelay_cm (
.IDATAIN(rd_clk_fb),
.TOUT(),
.DOUT(),
.T(1'b1),
.ODATAIN(1'b0),
.DATAOUT(rd_clk_fb_dly),
.DATAOUT2(),
.IOCLK0(1'b0),
.IOCLK1(1'b0),
.CLK(1'b0),
.CAL(1'b0),
.INC(1'b0),
.CE(1'b0),
.RST(1'b0),
.BUSY()
);
wire rd_clk_fb_dly_bufio;
BUFIO2 #(
.DIVIDE(1),
.DIVIDE_BYPASS("FALSE"),
.I_INVERT("FALSE")
) bufio2_inst (
.I(rd_clk_fb_dly),
.IOCLK(),
.DIVCLK(rd_clk_fb_dly_bufio),
.SERDESSTROBE()
);
wire pll2_lckd;
wire buf_pll2_fb_out;
wire pll2out0;
wire pll2out1;
PLL_ADV #(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT(4),
.CLKFBOUT_PHASE(0.0),
.CLKIN1_PERIOD(clk2x_period),
.CLKIN2_PERIOD(clk2x_period),
.CLKOUT0_DIVIDE(2),
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0.0),
.CLKOUT1_DIVIDE(2),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT1_PHASE(0.0),
.CLKOUT2_DIVIDE(7),
.CLKOUT2_DUTY_CYCLE(0.5),
.CLKOUT2_PHASE(0.0),
.CLKOUT3_DIVIDE(7),
.CLKOUT3_DUTY_CYCLE(0.5),
.CLKOUT3_PHASE(0.0),
.CLKOUT4_DIVIDE(7),
.CLKOUT4_DUTY_CYCLE(0.5),
.CLKOUT4_PHASE(0.0),
.CLKOUT5_DIVIDE(7),
.CLKOUT5_DUTY_CYCLE (0.5),
.CLKOUT5_PHASE(0.0),
.COMPENSATION("INTERNAL"),
.DIVCLK_DIVIDE(1),
.REF_JITTER(0.100),
.CLK_FEEDBACK("CLKFBOUT"),
.SIM_DEVICE("SPARTAN6")
) pll2 (
.CLKFBDCM(),
.CLKFBOUT(buf_pll2_fb_out),
.CLKOUT0(pll2out0), /* < x4 clock to capture read data */
.CLKOUT1(pll2out1), /* < x4 clock to capture read data */
.CLKOUT2(),
.CLKOUT3(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUTDCM0(),
.CLKOUTDCM1(),
.CLKOUTDCM2(),
.CLKOUTDCM3(),
.CLKOUTDCM4(),
.CLKOUTDCM5(),
.DO(),
.DRDY(),
.LOCKED(pll2_lckd),
.CLKFBIN(buf_pll2_fb_out),
.CLKIN1(rd_clk_fb_dly_bufio),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.DADDR(5'b00000),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'h0000),
.DWE(1'b0),
.RST(~pll1_lckd),
.REL(1'b0)
);
BUFPLL #(
.DIVIDE(4)
) rd_bufpll_left (
.PLLIN(pll2out0),
.GCLK(sys_clk),
.LOCKED(pll2_lckd),
.IOCLK(clk4x_rd_left),
.LOCK(),
.SERDESSTROBE(clk4x_rd_strb_left)
);
BUFPLL #(
.DIVIDE(4)
) rd_bufpll_right (
.PLLIN(pll2out1),
.GCLK(sys_clk),
.LOCKED(pll2_lckd),
.IOCLK(clk4x_rd_right),
.LOCK(),
.SERDESSTROBE(clk4x_rd_strb_right)
);
wire sdram_sys_clk_lock_d16;
reg sdram_sys_clk_lock_d17;
/*
* Async reset generation
* The reset is de-asserted 16 clocks after both internal clocks are locked.
*/
SRL16 reset_delay_sr(
.CLK(sys_clk),
.D(pll1_lckd & pll2_lckd),
.A0(1'b1),
.A1(1'b1),
.A2(1'b1),
.A3(1'b1),
.Q(sdram_sys_clk_lock_d16)
);
always @(posedge sys_clk)
sdram_sys_clk_lock_d17 <= sdram_sys_clk_lock_d16;
assign reset_n = sdram_sys_clk_lock_d17;
endmodule

View File

@ -1,62 +0,0 @@
/*
* Milkymist-NG SoC
* Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module m1reset(
input sys_clk,
input trigger_reset,
output reg sys_rst,
output ac97_rst_n,
output videoin_rst_n,
output flash_rst_n
);
reg [19:0] rst_debounce;
initial rst_debounce <= 20'hFFFFF;
initial sys_rst <= 1'b1;
always @(posedge sys_clk) begin
if(trigger_reset)
rst_debounce <= 20'hFFFFF;
else if(rst_debounce != 20'd0)
rst_debounce <= rst_debounce - 20'd1;
sys_rst <= rst_debounce != 20'd0;
end
assign ac97_rst_n = ~sys_rst;
assign videoin_rst_n = ~sys_rst;
/*
* We must release the Flash reset before the system reset
* because the Flash needs some time to come out of reset
* and the CPU begins fetching instructions from it
* as soon as the system reset is released.
* From datasheet, minimum reset pulse width is 100ns
* and reset-to-read time is 150ns.
*/
reg [7:0] flash_rstcounter;
initial flash_rstcounter <= 8'd0;
always @(posedge sys_clk) begin
if(trigger_reset)
flash_rstcounter <= 8'd0;
else if(~flash_rstcounter[7])
flash_rstcounter <= flash_rstcounter + 8'd1;
end
assign flash_rst_n = flash_rstcounter[7];
endmodule