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 // Released under the terms of the AGPL v3
// See the LICENSE file for full details // See the LICENSE file for full details
...@@ -14,9 +14,7 @@ ...@@ -14,9 +14,7 @@
module clock_generator( module clock_generator(
// Main 12MHz platform clock // Main 12MHz platform clock
input wire platform_clock, input wire platform_clock,
output wire platform_pll_lock, output wire spi_core_pll_lock,
output wire fast_48mhz_platform_clock,
output wire fast_24mhz_platform_clock,
output wire slow_1843khz_uart_clock, output wire slow_1843khz_uart_clock,
output wire slow_175khz_platform_clock, output wire slow_175khz_platform_clock,
output wire slow_183hz_platform_clock, output wire slow_183hz_platform_clock,
...@@ -25,34 +23,27 @@ module clock_generator( ...@@ -25,34 +23,27 @@ module clock_generator(
// Main 33MHz LPC clock // Main 33MHz LPC clock
input wire lpc_clock, input wire lpc_clock,
output wire sequencer_clock, output wire sequencer_clock,
output wire spi_core_clock,
input wire fast_24mhz_platform_clock_speed_ctl input wire reset_lpc_derived_plls
); );
`ifdef SIMULATION `ifdef SIMULATION
assign fast_48mhz_platform_clock = platform_clock; assign spi_core_clock = lpc_clock;
`else `else
// 48MHz fast platform clock // 66MHz SPI core clock
SB_PLL40_CORE #( SB_PLL40_CORE #(
.FEEDBACK_PATH("SIMPLE"), .FEEDBACK_PATH("SIMPLE"),
.PLLOUT_SELECT("GENCLK"), .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), .DIVR(4'b0000),
.DIVF(7'b0111111), .DIVF(7'b0001111),
.DIVQ(3'b100), .DIVQ(3'b011),
.FILTER_RANGE(3'b001) .FILTER_RANGE(3'b011)
`endif
) platform_pll ( ) platform_pll (
.LOCK(platform_pll_lock), .LOCK(spi_core_pll_lock),
.RESETB(1'b1), .RESETB(~reset_lpc_derived_plls),
.BYPASS(1'b0), .BYPASS(1'b0),
.REFERENCECLK(platform_clock), .REFERENCECLK(lpc_clock),
.PLLOUTGLOBAL(fast_48mhz_platform_clock) .PLLOUTGLOBAL(spi_core_clock)
); );
`endif `endif
...@@ -70,36 +61,10 @@ module clock_generator( ...@@ -70,36 +61,10 @@ module clock_generator(
// Slow clock generator // Slow clock generator
reg [20:0] slow_clock_counter = 0; reg [20:0] slow_clock_counter = 0;
always @(posedge fast_48mhz_platform_clock) begin always @(posedge platform_clock) begin
slow_clock_counter = slow_clock_counter + 1; slow_clock_counter = slow_clock_counter + 4;
end 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 // Buffer 1843Hz slow clock
SB_GB platform_1843khz_clock_buffer ( SB_GB platform_1843khz_clock_buffer (
.USER_SIGNAL_TO_GLOBAL_BUFFER(internal_1843khz_clock), .USER_SIGNAL_TO_GLOBAL_BUFFER(internal_1843khz_clock),
......
...@@ -158,6 +158,50 @@ module lpc_bridge_top( ...@@ -158,6 +158,50 @@ module lpc_bridge_top(
wire lpc_pll_lock; wire lpc_pll_lock;
wire lpc_slave_tx_clock_resynthesized; wire lpc_slave_tx_clock_resynthesized;
wire lpc_slave_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 #( SB_PLL40_2F_PAD #(
.FEEDBACK_PATH("PHASE_AND_DELAY"), .FEEDBACK_PATH("PHASE_AND_DELAY"),
.PLLOUT_SELECT_PORTA("SHIFTREG_0deg"), .PLLOUT_SELECT_PORTA("SHIFTREG_0deg"),
...@@ -174,6 +218,7 @@ module lpc_bridge_top( ...@@ -174,6 +218,7 @@ module lpc_bridge_top(
.PACKAGEPIN(lpc_slave_clock), .PACKAGEPIN(lpc_slave_clock),
.PLLOUTGLOBALA(lpc_slave_clock_resynthesized) .PLLOUTGLOBALA(lpc_slave_clock_resynthesized)
); );
`endif
assign lpc_slave_tx_clock_resynthesized = lpc_slave_clock_resynthesized; assign lpc_slave_tx_clock_resynthesized = lpc_slave_clock_resynthesized;
`else `else
wire lpc_slave_clock_buffered; wire lpc_slave_clock_buffered;
...@@ -215,31 +260,26 @@ module lpc_bridge_top( ...@@ -215,31 +260,26 @@ module lpc_bridge_top(
assign lpc_debug_mirror_clock_extern_uart = lpc_slave_clock_resynthesized; assign lpc_debug_mirror_clock_extern_uart = lpc_slave_clock_resynthesized;
// Instantiate clock generator // Instantiate clock generator
wire platform_pll_lock; wire spi_core_pll_lock;
wire platform_pll_unstable; wire platform_pll_unstable;
wire fast_48mhz_platform_clock;
wire fast_24mhz_platform_clock;
wire slow_1843khz_uart_clock; wire slow_1843khz_uart_clock;
wire slow_175khz_platform_clock; wire slow_175khz_platform_clock;
wire slow_183hz_platform_clock; wire slow_183hz_platform_clock;
wire slow_11hz_platform_clock; wire slow_11hz_platform_clock;
wire sequencer_clock; wire sequencer_clock;
wire spi_core_clock;
reg fast_24mhz_platform_clock_speed_ctl = 0;
`ifdef RESYNTHESIZE_LPC_CLOCK `ifdef RESYNTHESIZE_LPC_CLOCK
assign platform_pll_unstable = ~platform_pll_lock | ~lpc_pll_lock; assign platform_pll_unstable = ~lpc_pll_lock;
`else `else
assign platform_pll_unstable = ~platform_pll_lock; assign platform_pll_unstable = 0;
`endif `endif
clock_generator clock_generator( clock_generator clock_generator(
// Main 12MHz platform clock // Main 12MHz platform clock
.platform_clock(primary_platform_clock), .platform_clock(primary_platform_clock),
.platform_pll_lock(platform_pll_lock), .spi_core_pll_lock(spi_core_pll_lock),
.fast_48mhz_platform_clock(fast_48mhz_platform_clock),
.fast_24mhz_platform_clock(fast_24mhz_platform_clock),
.slow_1843khz_uart_clock(slow_1843khz_uart_clock), .slow_1843khz_uart_clock(slow_1843khz_uart_clock),
.slow_175khz_platform_clock(slow_175khz_platform_clock), .slow_175khz_platform_clock(slow_175khz_platform_clock),
.slow_183hz_platform_clock(slow_183hz_platform_clock), .slow_183hz_platform_clock(slow_183hz_platform_clock),
...@@ -248,8 +288,8 @@ module lpc_bridge_top( ...@@ -248,8 +288,8 @@ module lpc_bridge_top(
// Main 33MHz LPC clock // Main 33MHz LPC clock
.lpc_clock(lpc_slave_clock_resynthesized), .lpc_clock(lpc_slave_clock_resynthesized),
.sequencer_clock(sequencer_clock), .sequencer_clock(sequencer_clock),
.spi_core_clock(spi_core_clock),
.fast_24mhz_platform_clock_speed_ctl(fast_24mhz_platform_clock_speed_ctl) .reset_lpc_derived_plls(~lpc_pll_lock)
); );
// Generate power-on reset signals // Generate power-on reset signals
...@@ -629,7 +669,7 @@ module lpc_bridge_top( ...@@ -629,7 +669,7 @@ module lpc_bridge_top(
wire spi_external_master_hold_n_buffered; wire spi_external_master_hold_n_buffered;
spi_master_interface_quad spi_master_interface_quad( 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)), .reset(fpga_power_on_reset || (~lpc_slave_reset_n_buffered_gated)),
.tx_data(spi_external_master_tx_data), .tx_data(spi_external_master_tx_data),
.rx_data(spi_external_master_rx_data), .rx_data(spi_external_master_rx_data),
......
...@@ -16,6 +16,7 @@ ctx.addClock("fast_48mhz_platform_clock", 96) ...@@ -16,6 +16,7 @@ ctx.addClock("fast_48mhz_platform_clock", 96)
ctx.addClock("primary_platform_clock", 12) ctx.addClock("primary_platform_clock", 12)
ctx.addClock("sequencer_clock", 33) ctx.addClock("sequencer_clock", 33)
ctx.addClock("slow_1843khz_uart_clock", 2) 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 # 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. # sections of the LPC logic. Make sure both can run at LPC speeds.
......
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