Repurpose second PLL to generate a doubled clock for the SPI core

This significantly speeds up system IPL, and represents the fastest
practical IPL speed for this small dual-PLL FPGA.

Larger FPGAs such as the quad PLL ECP5 should be able to reach the
100+MHz quad SPI maximum frequency when used with properly laid out
parent 3808a6ee
// © 2017 Raptor Engineering, LLC
// © 2017 - 2020 Raptor Engineering, LLC
// Released under the terms of the AGPL v3
// See the LICENSE file for full details
......@@ -14,9 +14,7 @@
module clock_generator(
// Main 12MHz platform clock
input wire platform_clock,
output wire platform_pll_lock,
output wire fast_48mhz_platform_clock,
output wire fast_24mhz_platform_clock,
output wire spi_core_pll_lock,
output wire slow_1843khz_uart_clock,
output wire slow_175khz_platform_clock,
output wire slow_183hz_platform_clock,
......@@ -25,34 +23,27 @@ module clock_generator(
// Main 33MHz LPC clock
input wire lpc_clock,
output wire sequencer_clock,
input wire fast_24mhz_platform_clock_speed_ctl
output wire spi_core_clock,
input wire reset_lpc_derived_plls
assign fast_48mhz_platform_clock = platform_clock;
assign spi_core_clock = lpc_clock;
// 48MHz fast platform clock
// 66MHz SPI core clock
`ifdef USE_22_875MHZ_CLOCK
) platform_pll (
......@@ -70,36 +61,10 @@ module clock_generator(
// Slow clock generator
reg [20:0] slow_clock_counter = 0;
always @(posedge fast_48mhz_platform_clock) begin
slow_clock_counter = slow_clock_counter + 1;
always @(posedge platform_clock) begin
slow_clock_counter = slow_clock_counter + 4;
wire internal_24mhz_clock;
assign internal_24mhz_clock = fast_48mhz_platform_clock;
// 24MHz fast platform clock
reg internal_24mhz_clock = 0;
reg internal_ref_24mhz_clock = 0;
always @(posedge fast_48mhz_platform_clock) begin
internal_ref_24mhz_clock = ~internal_ref_24mhz_clock;
if (fast_24mhz_platform_clock_speed_ctl) begin
// 87.5KHz slumber mode clock
internal_24mhz_clock <= slow_clock_counter[6];
end else begin
internal_24mhz_clock <= internal_ref_24mhz_clock;
// Buffer 24MHz fast clock
SB_GB platform_24mhz_clock_buffer (
// Buffer 1843Hz slow clock
SB_GB platform_1843khz_clock_buffer (
......@@ -158,6 +158,50 @@ module lpc_bridge_top(
wire lpc_pll_lock;
wire lpc_slave_tx_clock_resynthesized;
wire lpc_slave_clock_resynthesized;
// NOTE:
// The iCE40 PLL is very poorly documented in the
// external feedback path. Discussion follows:
// In the iCE40 Ultra Plus documentation, it is mentioned
// that EXTERNAL_DIVIDE_FACTOR actually influences the clock
// synthesis by acting as a multiplier for GENCLK.
// Therefore, to get a doubled frequency along with the phase
// aligned / duty cycle corrected original frequency, the PLL
// needs to be set for 1:1 clock synthesis and the feedback
// path division factor needs to be set to 2. Feeding the
// GENCLK_HALF signal back into the feedback pin then seems
// to work as intended -- the original clock is aligned, and
// a doubled clock is available on port B via the GENCLK route.
// Unfortunately, even with this inferred information, the PLL
// is not exactly phase matching its output with the input.
// Since this is apparently a little used feature of the iCE40
// PLLs, it's probably not worth debugging in this context.
// Larger devices like the ECP5 have four PLLs to work with,
// making the use of a second PLL to generate the SPI clock
// more reasonable.
wire lpc_slave_clock_doubled;
SB_PLL40_2F_PAD #(
) lpc_slave_pll (
SB_PLL40_2F_PAD #(
......@@ -174,6 +218,7 @@ module lpc_bridge_top(
assign lpc_slave_tx_clock_resynthesized = lpc_slave_clock_resynthesized;
wire lpc_slave_clock_buffered;
......@@ -215,31 +260,26 @@ module lpc_bridge_top(
assign lpc_debug_mirror_clock_extern_uart = lpc_slave_clock_resynthesized;
// Instantiate clock generator
wire platform_pll_lock;
wire spi_core_pll_lock;
wire platform_pll_unstable;
wire fast_48mhz_platform_clock;
wire fast_24mhz_platform_clock;
wire slow_1843khz_uart_clock;
wire slow_175khz_platform_clock;
wire slow_183hz_platform_clock;
wire slow_11hz_platform_clock;
wire sequencer_clock;
reg fast_24mhz_platform_clock_speed_ctl = 0;
wire spi_core_clock;
assign platform_pll_unstable = ~platform_pll_lock | ~lpc_pll_lock;
assign platform_pll_unstable = ~lpc_pll_lock;
assign platform_pll_unstable = ~platform_pll_lock;
assign platform_pll_unstable = 0;
clock_generator clock_generator(
// Main 12MHz platform clock
......@@ -248,8 +288,8 @@ module lpc_bridge_top(
// Main 33MHz LPC clock
// Generate power-on reset signals
......@@ -629,7 +669,7 @@ module lpc_bridge_top(
wire spi_external_master_hold_n_buffered;
spi_master_interface_quad spi_master_interface_quad(
.reset(fpga_power_on_reset || (~lpc_slave_reset_n_buffered_gated)),
......@@ -16,8 +16,9 @@ ctx.addClock("fast_48mhz_platform_clock", 96)
ctx.addClock("primary_platform_clock", 12)
ctx.addClock("sequencer_clock", 33)
ctx.addClock("slow_1843khz_uart_clock", 2)
ctx.addClock("spi_core_clock", lpc_clock_frequency * 2)
# For some reason both of these clocks are showing up, for presumably different
# sections of the LPC logic. Make sure both can run at LPC speeds.
ctx.addClock("lpc_slave_tx_clock_resynthesized", lpc_clock_frequency)
ctx.addClock("lpc_debug_mirror_clock_extern_uart", lpc_clock_frequency)
ctx.addClock("lpc_debug_mirror_clock_extern_uart", lpc_clock_frequency)
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment