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
PCBs.
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
);
`ifdef SIMULATION
assign fast_48mhz_platform_clock = platform_clock;
assign spi_core_clock = lpc_clock;
`else
// 48MHz fast platform clock
// 66MHz SPI core clock
SB_PLL40_CORE #(
.FEEDBACK_PATH("SIMPLE"),
.PLLOUT_SELECT("GENCLK"),
`ifdef USE_22_875MHZ_CLOCK
.DIVR(4'b0000),
.DIVF(7'b0111100),
.DIVQ(3'b100),
.FILTER_RANGE(3'b001)
`else
.DIVR(4'b0000),
.DIVF(7'b0111111),
.DIVQ(3'b100),
.FILTER_RANGE(3'b001)
`endif
.DIVF(7'b0001111),
.DIVQ(3'b011),
.FILTER_RANGE(3'b011)
) platform_pll (
.LOCK(platform_pll_lock),
.RESETB(1'b1),
.LOCK(spi_core_pll_lock),
.RESETB(~reset_lpc_derived_plls),
.BYPASS(1'b0),
.REFERENCECLK(platform_clock),
.PLLOUTGLOBAL(fast_48mhz_platform_clock)
.REFERENCECLK(lpc_clock),
.PLLOUTGLOBAL(spi_core_clock)
);
`endif
......@@ -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;
end
`ifdef SIMULATION
wire internal_24mhz_clock;
assign internal_24mhz_clock = fast_48mhz_platform_clock;
`else
// 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;
end
end
`endif
// Buffer 24MHz fast clock
SB_GB platform_24mhz_clock_buffer (
.USER_SIGNAL_TO_GLOBAL_BUFFER(internal_24mhz_clock),
.GLOBAL_BUFFER_OUTPUT(fast_24mhz_platform_clock)
);
// Buffer 1843Hz slow clock
SB_GB platform_1843khz_clock_buffer (
.USER_SIGNAL_TO_GLOBAL_BUFFER(internal_1843khz_clock),
......
......@@ -158,6 +158,50 @@ module lpc_bridge_top(
wire lpc_pll_lock;
wire lpc_slave_tx_clock_resynthesized;
wire lpc_slave_clock_resynthesized;
`ifdef ENABLE_EXPERIMENTAL_RECLOCKING_PLL_DOUBLING
// 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 #(
.FEEDBACK_PATH("EXTERNAL"),
.PLLOUT_SELECT_PORTA("GENCLK_HALF"),
.PLLOUT_SELECT_PORTB("GENCLK"),
.EXTERNAL_DIVIDE_FACTOR(2'b10),
.SHIFTREG_DIV_MODE(1'b0),
.DIVR(4'b0000),
.DIVF(7'b0000000),
.DIVQ(3'b100),
.FILTER_RANGE(3'b011)
) lpc_slave_pll (
.LOCK(lpc_pll_lock),
.EXTFEEDBACK(lpc_slave_clock_resynthesized),
.RESETB(lpc_slave_reset_n_buffered_gated),
.BYPASS(1'b0),
.PACKAGEPIN(lpc_slave_clock),
.PLLOUTGLOBALA(lpc_slave_clock_resynthesized),
.PLLOUTGLOBALB(lpc_slave_clock_doubled)
);
`else
SB_PLL40_2F_PAD #(
.FEEDBACK_PATH("PHASE_AND_DELAY"),
.PLLOUT_SELECT_PORTA("SHIFTREG_0deg"),
......@@ -174,6 +218,7 @@ module lpc_bridge_top(
.PACKAGEPIN(lpc_slave_clock),
.PLLOUTGLOBALA(lpc_slave_clock_resynthesized)
);
`endif
assign lpc_slave_tx_clock_resynthesized = lpc_slave_clock_resynthesized;
`else
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;
`ifdef RESYNTHESIZE_LPC_CLOCK
assign platform_pll_unstable = ~platform_pll_lock | ~lpc_pll_lock;
assign platform_pll_unstable = ~lpc_pll_lock;
`else
assign platform_pll_unstable = ~platform_pll_lock;
assign platform_pll_unstable = 0;
`endif
clock_generator clock_generator(
// Main 12MHz platform clock
.platform_clock(primary_platform_clock),
.platform_pll_lock(platform_pll_lock),
.fast_48mhz_platform_clock(fast_48mhz_platform_clock),
.fast_24mhz_platform_clock(fast_24mhz_platform_clock),
.spi_core_pll_lock(spi_core_pll_lock),
.slow_1843khz_uart_clock(slow_1843khz_uart_clock),
.slow_175khz_platform_clock(slow_175khz_platform_clock),
.slow_183hz_platform_clock(slow_183hz_platform_clock),
......@@ -248,8 +288,8 @@ module lpc_bridge_top(
// Main 33MHz LPC clock
.lpc_clock(lpc_slave_clock_resynthesized),
.sequencer_clock(sequencer_clock),
.fast_24mhz_platform_clock_speed_ctl(fast_24mhz_platform_clock_speed_ctl)
.spi_core_clock(spi_core_clock),
.reset_lpc_derived_plls(~lpc_pll_lock)
);
// 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(
.platform_clock(lpc_slave_clock_resynthesized),
.platform_clock(spi_core_clock),
.reset(fpga_power_on_reset || (~lpc_slave_reset_n_buffered_gated)),
.tx_data(spi_external_master_tx_data),
.rx_data(spi_external_master_rx_data),
......
......@@ -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
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment