Maximilian
Maximilian

Reputation: 175

ice40hx8k pll in VHDL

I am using an iceFUN FPGA board and have a working design that blinks an LED at 1 Hz. It is using the external 12 Mhz clock connected on GBIN5.

I want to instantiate a pll to increase the internal frequency to around 50Mhz. without further modifying the design I would expect the led to blink at ~4.166Hz

How do I configure/instantiate one of the two plls the ice40hx8k offers?

The VHDL code I am using to blink the LED is as follows:

-- top.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

-- iceFUN FPGA (ice40hx8k package 132)
-- the board has an external 12Mhz clock on GBIN5 (P7)

--  Led positions
--                .----------- C1
--               |   .-------- C2
--               |  |  .------ C3
--               |  |  |  .--- C4
--               |  |  |  |
-- +---+ +---+  R1 R1 R1 R1         
-- |   | |   |  R2 R2 R2 R2         
-- |   | |   |  R3 R3 R3 R3         +====
-- +---+ +---+  R4 R4 R4 R4         | USB
-- +---+ +---+  R5 R5 R5 R5         | USB
-- |   | |   |  R6 R6 R6 R6         +====
-- |   | |   |  R7 R7 R7 R7         
-- +---+ +---+  R8 R8 R8 R8         

-- the led must be LOW and the corresponding column (row) be LOW to turn on the led

entity top is
    port (
        clk : in std_logic;
        R1, R2, R3, R4, R5, R6, R7, R8, C1, C2, C3, C4 : out std_logic
    );
end top;

architecture rtl of top is
    signal blink : std_logic := '1';
begin

    process (clk)
    variable counter : unsigned (23 downto 0) := x"000000";
    begin
    if rising_edge(clk) then
        if counter = 11_999_999 then
            counter := x"000000";
            blink <= not blink;
        else
            counter := counter + 1;
        end if;
    end if;
    end process;

    R1 <= blink;
    R2 <= '1';
    R3 <= '1';
    R4 <= '1';
    R5 <= '1';
    R6 <= '1';
    R7 <= '1';
    R8 <= '1';

    C1 <=  '0';
    C2 <=  '1';
    C3 <=  '1';
    C4 <=  '1';
end rtl;

And I synthesize the design with:

yosys -m ghdl -p 'ghdl --std=08 top.vhd -e top; synth_ice40 -json top.json'
nextpnr-ice40 -r --hx8k --json top.json --package cb132 --asc top.asc --opt-timing --pcf iceFUN.pcf --report bin/usage.report.json
icepack top.asc top.bin

For completeness the pin config

#iceFUN.pcf
# For iceFUN board

set_io --warn-no-port clk        P7
set_io --warn-no-port R1         C10
set_io --warn-no-port R2         A10
set_io --warn-no-port R3         D7
set_io --warn-no-port R4         D6
set_io --warn-no-port R5         A7
set_io --warn-no-port R6         C7
set_io --warn-no-port R7         A4
set_io --warn-no-port R8         C4
set_io --warn-no-port C1         A12
set_io --warn-no-port C2         D10
set_io --warn-no-port C3         A6
set_io --warn-no-port C4         C5

set_io --warn-no-port SPI_CS     C3
set_io --warn-no-port SPI_SCK    A1
set_io --warn-no-port SPI_MOSI   A2
set_io --warn-no-port SPI_MISO   A3

I can produce some verilog code to configure a pll with:

$ icepll -p -i 12 -o 50 -m
/**
 * 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:   50.000 MHz
 * Achieved output frequency:    50.250 MHz
 */

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

SB_PLL40_PAD #(
        .FEEDBACK_PATH("SIMPLE"),
        .DIVR(4'b0000),     // DIVR =  0
        .DIVF(7'b1000010),  // DIVF = 66
        .DIVQ(3'b100),      // DIVQ =  4
        .FILTER_RANGE(3'b001)   // FILTER_RANGE = 1
    ) uut (
        .LOCK(locked),
        .RESETB(1'b1),
        .BYPASS(1'b0),
        .PACKAGEPIN(clock_in),
        .PLLOUTCORE(clock_out)
        );

endmodule

I imagine the VHDL instance code to be something like:

    pll_instance : entity work.pll
    port map(
        input => clk,
        output => fast_clk,
        locked => locked
    );

where clk is the original 12Mhz clock and fask_clk is a new signal to carry the 50Mhz clock.

However, I do not know how to add the verilog code to my synthesis steps.

Upvotes: 0

Views: 86

Answers (1)

Maximilian
Maximilian

Reputation: 175

After much searching I stumbled across a solution

A pull request that was never merged implemented the VHDL code generation I was missing to the icepll tool: https://github.com/YosysHQ/icestorm/pull/265

Something like this VHDL file would be created. When added to the sources and instantiated in the top it provides the desired pll.

I did not fund out why the pull request never got merged. Maybe the devs wanted to focus only on verilog?

-- pll.vhd
--
-- PLL configuration
--
-- This VHDL entity was generated automatically
-- using the icepll tool from the IceStorm project.
-- Use at your own risk.
-- 
-- F_PLLIN:    12.000 MHz (given)
-- F_PLLOUT:   50.000 MHz (requested)
-- F_PLLOUT:   50.250 MHz (achieved)
--

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity pll is
    port (
        clk_in  :  in std_logic;
        clk_out : out std_logic;
        locked  : out std_logic
    );
end pll;

architecture synth of pll is
    component SB_PLL40_CORE is
        generic (
            FEEDBACK_PATH : String := "SIMPLE";
            DIVR : unsigned(3 downto 0) := "0000";--\n", binstr(best_divr, 4));
            DIVF : unsigned(6 downto 0) := "1000010";--\n", binstr(best_divf, 7));
            DIVQ : unsigned(2 downto 0) := "100";--\n", binstr(best_divq, 3));
            FILTER_RANGE : unsigned(2 downto 0) := "001"--\n", binstr(filter_range, 3));
        );
        port (
            LOCK : out std_logic;
            RESETB : in std_logic;
            BYPASS : in std_logic;
            REFERENCECLK : in std_logic;
            PLLOUTGLOBAL : out std_logic
        );
    end component;

begin
    PLL1 : SB_PLL40_CORE port map (
        LOCK => locked,
        RESETB => '1',
        BYPASS => '0',
        REFERENCECLK => clk_in,
        PLLOUTGLOBAL => clk_out
    );
end;

Upvotes: 0

Related Questions