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
- two pin drivers
- a pll
- two clock dividers (see https://hackaday.io/project/162557-fpga-dabbling/log/156515-1-blinky for clockdivider.v)
- and some glue code to put everything together
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
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.