From 930f9f70787b491c2436382ed67873a6ad3946a1 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 6 Sep 2024 10:04:50 -0700 Subject: [PATCH] sentinel65x: start of Sentinel 65X top-level glue, with a PLL module The PLL generates a 100MHz clock for GARY, 10MHz for the CPU and system bus, and 25MHz for VGA out. --- sentinel65x/PLL.bsv | 49 ++++++++++++++++++++++++++++++++++++ sentinel65x/PLL.v | 60 +++++++++++++++++++++++++++++++++++++++++++++ tasks.py | 18 ++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 sentinel65x/PLL.bsv create mode 100644 sentinel65x/PLL.v diff --git a/sentinel65x/PLL.bsv b/sentinel65x/PLL.bsv new file mode 100644 index 0000000..4cd058b --- /dev/null +++ b/sentinel65x/PLL.bsv @@ -0,0 +1,49 @@ +package PLL; + +// Frequencies: FPGA 100MHz, CPU 10MHz, VGA 25MHz +// +// To regenerate, see 'inv genclk -h' + +// Clocks are the various clocks used in GARY. +interface Clocks; + // main_clk is the internal clock that most of the design runs at. + interface Clock main_clk; + // cpu_clk is the clock generated and output for use by the 65X + // CPU. The system bus interface is clocked by cpu_clk. + interface Clock cpu_clk; + // vga_clk is the pixel clock for the video output. + interface Clock vga_clk; + + // locked is whether the PLL has locked and is producing stable + // clocks. + // + // TODO: hook this into clock gating or global reset, to hold the + // FPGA in a safe state while the PLL starts up and locks. + (* always_ready *) + method Bool locked(); +endinterface + +// mkPLL takes in a reference clock signal and generates GARY's +// clocks. All the output clocks are in phase with main_clk, +// i.e. every posedge of cpu_clk and vga_clk is also a posedge of +// main_clk. +import "BVI" PLL = + module mkPLL(Clock clk, Clocks ifc); + default_clock no_clock; + default_reset no_reset; + + input_clock ref_clk(CLK_REF, (* unused *)CLK_REF_GATE) = clk; + + output_clock main_clk(CLK_MAIN); + output_clock cpu_clk(CLK_CPU); + output_clock vga_clk(CLK_VGA); + + ancestor(cpu_clk, main_clk); + ancestor(vga_clk, main_clk); + + method locked locked(); + + schedule locked CF locked; + endmodule + +endpackage diff --git a/sentinel65x/PLL.v b/sentinel65x/PLL.v new file mode 100644 index 0000000..85868a7 --- /dev/null +++ b/sentinel65x/PLL.v @@ -0,0 +1,60 @@ +// diamond 3.7 accepts this PLL +// diamond 3.8-3.9 is untested +// diamond 3.10 or higher is likely to abort with error about unable to use feedback signal +// cause of this could be from wrong CPHASE/FPHASE parameters +module PLL +( + input CLK_REF, // 25 MHz, 0 deg + output CLK_MAIN, // 100 MHz, 0 deg + output CLK_CPU, // 10 MHz, 0 deg + output CLK_VGA, // 25 MHz, 0 deg + output locked +); +(* FREQUENCY_PIN_CLKI="25" *) +(* FREQUENCY_PIN_CLKOP="100" *) +(* FREQUENCY_PIN_CLKOS="10" *) +(* FREQUENCY_PIN_CLKOS2="25" *) +(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *) +EHXPLLL #( + .PLLRST_ENA("DISABLED"), + .INTFB_WAKE("DISABLED"), + .STDBY_ENABLE("DISABLED"), + .DPHASE_SOURCE("DISABLED"), + .OUTDIVIDER_MUXA("DIVA"), + .OUTDIVIDER_MUXB("DIVB"), + .OUTDIVIDER_MUXC("DIVC"), + .OUTDIVIDER_MUXD("DIVD"), + .CLKI_DIV(1), + .CLKOP_ENABLE("ENABLED"), + .CLKOP_DIV(6), + .CLKOP_CPHASE(2), + .CLKOP_FPHASE(0), + .CLKOS_ENABLE("ENABLED"), + .CLKOS_DIV(60), + .CLKOS_CPHASE(2), + .CLKOS_FPHASE(0), + .CLKOS2_ENABLE("ENABLED"), + .CLKOS2_DIV(24), + .CLKOS2_CPHASE(2), + .CLKOS2_FPHASE(0), + .FEEDBK_PATH("CLKOP"), + .CLKFB_DIV(4) + ) pll_i ( + .RST(1'b0), + .STDBY(1'b0), + .CLKI(CLK_REF), + .CLKOP(CLK_MAIN), + .CLKOS(CLK_CPU), + .CLKOS2(CLK_VGA), + .CLKFB(CLK_MAIN), + .CLKINTFB(), + .PHASESEL0(1'b0), + .PHASESEL1(1'b0), + .PHASEDIR(1'b1), + .PHASESTEP(1'b1), + .PHASELOADREG(1'b1), + .PLLWAKESYNC(1'b0), + .ENCLKOP(1'b0), + .LOCK(locked) + ); +endmodule diff --git a/tasks.py b/tasks.py index 1463224..3c68632 100644 --- a/tasks.py +++ b/tasks.py @@ -202,3 +202,21 @@ def test(c, target): def clean(c): if Path("out").is_dir(): shutil.rmtree("out") + +@task +def genclk(c, in_mhz=25, main_mhz=100, cpu_mhz=10, vga_mhz=25): + out = Path("gary/PLL.v") + c.run(f"ecppll -f {out} --module PLL --clkin_name CLK_REF --clkin {in_mhz} --clkout0_name CLK_MAIN --clkout0 {main_mhz} --clkout1_name CLK_CPU --clkout1 {cpu_mhz} --clkout2_name CLK_VGA --clkout2 {vga_mhz}") + bsv_out = out.with_suffix(".bsv") + with open(bsv_out) as f: + lines = f.readlines() + idx = None + for i, l in enumerate(lines): + if l.startswith("// Frequencies: "): + idx = i + break + if idx is None: + print(f"WARNING: couldn't find frequencies line in {bsv_out}, not updating") + lines[i] = f"// Frequencies: FPGA {main_mhz}MHz, CPU {cpu_mhz}MHz, VGA {vga_mhz}MHz\n" + with open(bsv_out, "w") as f: + f.write("".join(lines))