Close

#2: still blinky, but now adding a PLL clock

A project log for FPGA dabbling

I don't need an FPGA for anything but the fun of experimenting with it.

christophChristoph 11/30/2018 at 20:220 Comments

So with the first blinky test behind me, I wanted to get away from the fixed 12 MHz external oscillator. The HX1K has a PLL for that! For someone with a background in mechanical engineering, the act of creating a faster clock than we originally had is somewhat mind-blowing, so let's go for 24 MHz.

Instead of making the mistake of reading the datasheet and then failing to make sense of it, let's use icepll to create code for the PLL:

icepll -i 12 -o 24 -f pll.v -m

 here's the output, which also kinda describes what the command line requests:

F_PLLIN:    12.000 MHz (given)
F_PLLOUT:   24.000 MHz (requested)
F_PLLOUT:   24.000 MHz (achieved)

FEEDBACK: SIMPLE
F_PFD:   12.000 MHz
F_VCO:  768.000 MHz

DIVR:  0 (4'b0000)
DIVF: 63 (7'b0111111)
DIVQ:  5 (3'b101)

FILTER_RANGE: 1 (3'b001)

PLL configuration written to: pll.v

The file that was created contains a ready to use PLL module.

pll.v

/**
 * PLL configuration
 *
 * This Verilog module was generated automatically
 * using the icepll tool from the IceStorm project.
 * Use at your own risk.
 *
 * Given input frequency:        12.000 MHz
 * Requested output frequency:   24.000 MHz
 * Achieved output frequency:    24.000 MHz
 */

module pll(
	input  clock_in,
	output clock_out,
	output locked
	);

SB_PLL40_CORE #(
		.FEEDBACK_PATH("SIMPLE"),
		.DIVR(4'b0000),		// DIVR =  0
		.DIVF(7'b0111111),	// DIVF = 63
		.DIVQ(3'b101),		// DIVQ =  5
		.FILTER_RANGE(3'b001)	// FILTER_RANGE = 1
	) uut (
		.LOCK(locked),
		.RESETB(1'b1),
		.BYPASS(1'b0),
		.REFERENCECLK(clock_in),
		.PLLOUTCORE(clock_out)
		);

endmodule

I can now use pll.v in my top-level module. 

Since I now have two clocks, I also want two LEDs. Since there's an RGB LED on my board, let's go for that.

top.pcf

set_io clk_12M 21
set_io pin_ledr 97
set_io pin_ledg 99

So there's still the input from the external oscillator, but this time I have two pins for - well - two LEDs.

The top level module now contains

To me it's pretty obvious that a slightly different structure would have made it easier to read.

top.v

module top (
  input clk_12M,
  output pin_ledr,
  output pin_ledg
);

reg led_r = 0;
SB_IO #(
  .PIN_TYPE(6'b011001)
) pin_ledr_driver (
  .PACKAGE_PIN(pin_ledr),
  .D_OUT_0(led_r)
);

wire clk_r; // for pin_ledr
clockdivider #(.bits(28)) div_r(
  .clkin(clk_12M),
  .clkout(clk_r),
  .div(28'd12000000)
);

always @(posedge clk_r)
led_r = ~led_r;

wire clk_24M;
pll pll(
  .clock_in(clk_12M),
  .clock_out(clk_24M)
);

reg led_g = 0;
SB_IO #(
  .PIN_TYPE(6'b011001)
) pin_ledg_driver (
  .PACKAGE_PIN(pin_ledg),
  .D_OUT_0(led_g)
);

wire clk_g; // for pin_ledr
clockdivider #(.bits(28)) div_g(
  .clkin(clk_24M),
  .clkout(clk_g),
  .div(28'd12000000)
);

always @(posedge clk_g)
led_g = ~led_g;

endmodule

synthesize, place&route and pack:

yosys -p 'synth_ice40 -top top -blif top.blif' top.v clockdivider.v pll.v
arachne-pnr -d 1k -o top.asc -p top.pcf top.blif
icepack top.asc top.bin

 download:

iceprog -S top.bin

Double blinky indeed! Two LEDs are now blinking at two different frequencies, and they're so bright that I unplugged the board again. I'll need some sort of PWM to dim that. An obvious choice for the next experiment.

Discussions