From bbd4b3defc62d0047ebd5afcf125e4977a35b9b4 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 15 Nov 2022 04:50:22 +1000 Subject: [PATCH] Add initial PLL support (#133) * Add initial PLL support This PLL only works on the GW1N-1 chip and understands only two fixed frequencies: 52 and 56 MHz. Dynamic divider tuning is not supported. Signed-off-by: YRabbit * Add an example for PLL Only for owners of Tangnano board and oscilloscope. Signed-off-by: YRabbit * Add unpacker for PLL At the moment it is not clear how it will behave with large chips with multiple PLLs and individual parts arranged differently in relation to each other, but for GW1N-1 it will work reasonably well as a pair to gowin_pack. And keep in mind that unpacking images made with vendor IDE will be wrong in terms of wiring - remember we are currently using routing over general purpose wires, not special (mostly unexplored) lines. Signed-off-by: YRabbit * Add support for a more common chip The GW1N-1 and GW1NZ-1 have a similar PLL, but the board with the former chip is already very hard to buy, so let's experiment with a more affordable chip. Signed-off-by: YRabbit * Add missing files for PLL examples Signed-off-by: YRabbit Signed-off-by: YRabbit Co-authored-by: Pepijn de Vos --- apycula/attrids.py | 603 ++++++++++++++++++++++++++++++++++++++++ apycula/chipdb.py | 180 +++++++++++- apycula/gowin_pack.py | 165 ++++++++++- apycula/gowin_unpack.py | 129 ++++++++- examples/Makefile | 7 +- examples/pll.v | 48 ++++ examples/pll/GW1N-1.vh | 6 + examples/pll/GW1NZ-1.vh | 6 + 8 files changed, 1135 insertions(+), 9 deletions(-) create mode 100644 apycula/attrids.py create mode 100644 examples/pll.v create mode 100644 examples/pll/GW1N-1.vh create mode 100644 examples/pll/GW1NZ-1.vh diff --git a/apycula/attrids.py b/apycula/attrids.py new file mode 100644 index 00000000..a8edceef --- /dev/null +++ b/apycula/attrids.py @@ -0,0 +1,603 @@ +# These constants are used to interact with the 'logicinfo', 'shortval' and maybe 'longval' tables +# * - it seems that these attributes must always be present (XXX) +# Warning: +# - the names assigned to constants are not exact correspondence with the primitive parameter names +# - some constants are for internal use only +# - not all attribute values have names because for example naming all values from 0 to 63 is tedious. +iob_attrids = { + 'IO_TYPE': 0, + 'SLEWRATE': 1, # * + 'PULLMODE': 2, # * + 'DRIVE': 3, # * + 'OPENDRAIN': 4, # * + 'HYSTERESIS': 5, # * + 'CLAMP': 6, # * + 'DIFFRESISTOR': 7, # * + 'SINGLERESISTOR': 8, # * + 'VREF': 9, # * + 'VCCIO': 10, + 'DIFFDRIVE': 11, + 'I3C_MODE': 12, + 'MIPI_INPUT': 13, + 'MIPI_OUTPUT': 14, + 'DRIVE_LEVEL': 15, + 'LVDS_OUT': 16, # * + 'LVDS_VCCIO': 17, + 'DDR_DYNTERM': 18, + 'IO_BANK': 19, # * + 'PERSISTENT': 20, # * + 'TO': 21, + 'ODMUX': 22, + 'ODMUX_1': 23, + 'PADDI': 24, + 'PG_MUX': 25, + 'DATA_MUX': 26, + 'TRI_MUX': 27, + 'TRIMUX_PADDT': 28, + 'IOBUF_PADDI': 29, + 'USED': 30, # * + 'IOBUF_OVERDRIVE': 31, + 'IOBUF_UNDERDRIVE': 32, + 'IOBUF_LVDS25_VCCIO': 33, + 'IN12_MODE': 34, + 'OD': 35, + 'LPRX_A1': 36, + 'LPRX_A2': 37, + 'MIPI': 38, + 'LVDS_SEL': 39, + 'VLDS_ON': 40, + 'IOBUF_MIPI_LP': 41, + 'IOBUF_ODT_RESISTOR': 42, + 'IOBUF_CIB_CONTROL': 43, + 'IOBUF_INR_MODE': 44, + 'IOBUF_STDBY_LVDS_MODE': 45, + 'IOBUF_IODUTY': 46, + 'IOBUF_ODT_DYNTERM': 47, + 'MIPI_IBUF_DRIVE': 48, + 'MIPI_IBUF_DRIVE_LEVEL': 49 + } + +iob_attrvals = { + 'UNKNOWN': 0, # possible a dummy value for line 0 in logicinfo? + # standard + 'MIPI': 1, + 'BLVDS25E': 2, + 'BLVDS25': 3, + 'BLVDS_E': 4, + 'HSTL': 5, + 'HSTL_D': 6, + 'HSTL15_I': 7, + 'HSTL15D_I': 8, + 'HSTL18_I': 9, + 'HSTL18_II': 10, + 'HSTL18D_I': 11, + 'HSTL18D_II': 12, + 'SSTL': 13, + 'SSTL_D': 14, + 'SSTL15': 15, + 'SSTL15D': 16, + 'SSTL18_I': 17, + 'SSTL18_II': 18, + 'SSTL18D_I': 19, + 'SSTL18D_II': 20, + 'SSTL25_I': 21, + 'SSTL25_II': 22, + 'SSTL25D_I': 23, + 'SSTL25D_II': 24, + 'SSTL33_I': 25, + 'SSTL33_II': 26, + 'SSTL33D_I': 27, + 'SSTL33D_II': 28, + 'LVCMOS12': 29, + 'LVCMOS15': 30, + 'LVCMOS18': 31, + 'LVCMOS25': 32, + 'LVCMOS33': 33, + 'LVCMOS_D': 34, + 'LVCMOS12D': 35, + 'LVCMOS15D': 36, + 'LVCMOS18D': 37, + 'LVCMOS25D': 38, + 'LVCMOS33D': 39, + 'LVDS': 40, + 'LVDS_E': 41, + 'LVDS25': 42, + 'LVDS25E': 43, + 'LVPECL33': 44, + 'LVPECL33E': 45, + 'LVTTL33': 46, + 'MLVDS25': 47, + 'MLVDS_E': 48, + 'MLVDS25E': 49, + 'RSDS25E': 50, + 'PCI33': 51, + 'RSDS': 52, + 'RSDS25': 53, + 'RSDS_E': 54, + 'MINILVDS': 55, + 'PPLVDS': 56, + # + 'VREF1_DRIVER': 57, + 'VREF2_DRIVER': 58, + 'LVCMOS33OD25': 59, + 'LVCMOS33OD18': 60, + 'LVCMOS33OD15': 61, + 'LVCMOS25OD18': 62, + 'LVCMOS25OD15': 63, + 'LVCMOS18OD15': 64, + 'LVCMOS15OD12': 65, + 'LVCMOS25UD33': 66, + 'LVCMOS18UD25': 67, + 'LVCMOS18UD33': 68, + 'LVCMOS15UD18': 69, + 'LVCMOS15UD25': 70, + 'LVCMOS15UD33': 71, + 'LVCMOS12UD15': 72, + 'LVCMOS12UD18': 73, + 'LVCMOS12UD25': 74, + 'LVCMOS12UD33': 75, + 'VREF1_LOAD': 76, + 'VREF2_LOAD': 77, + # + 'ENABLE': 78, + 'TRIMUX': 79, + 'PADDI': 80, + 'PGBUF': 81, + '0': 82, + '1': 83, + 'SIG': 84, + 'INV': 85, + 'TO': 86, + # voltage + '1.2': 87, + '1.25': 88, + '1.5': 89, + '1.8': 90, + '2.0': 91, + '2.5': 92, + '3.3': 93, + '3.5': 94, + # mA + '2': 95, + '4': 96, + '6': 97, + '8': 98, + '12': 99, + '16': 100, + '20': 101, + '24': 102, + # XXX ? + '80': 103, + '100': 104, + '120': 105, + # + 'NA': 106, + 'ON': 107, + 'OFF': 108, + # XXX + 'PCI': 109, + # histeresis + 'HIGH': 110, + 'H2L': 111, + 'L2H': 112, + # pullmode + 'DOWN': 113, + 'KEEPER': 114, + 'NONE': 115, + 'UP': 116, + # slew + 'FAST': 117, + 'SLOW': 118, + # + 'I45': 119, + 'I50': 120, + 'I55': 121, + 'TSREG': 122, + 'TMDDR': 123, + 'OD1': 124, + 'OD2': 125, + 'OD3': 126, + 'UD1': 127, + 'UD3': 128, + # resistor? + 'INTERNAL': 129, + 'SINGLE': 130, + 'DIFF': 131, + # + 'IN12': 132, + 'UD2': 133, + 'LVPECL_E': 134, + # + '68': 135, + '3': 136, + '5': 137, + '7': 138, + '9': 139, + '10': 140, + '11': 141, + '4.5': 142, + 'MIPI_IBUF': 143, + '1.35': 144, + '5.5': 145, + '6.5': 146, + '10.5': 147, + '13.5': 148, + '14': 149, + # more standard + 'TMDS33': 150, + 'LPDDR': 151, + 'HSUL12': 152, + 'HSUL12D': 153, + 'HSTL12_I': 154, + 'HSTL15_II': 155, + 'HSTL15D_II': 156, + 'SSTL12': 157, + 'SSTL135': 158, + 'SSTL135D': 159, + 'LVCMOS10': 160, + 'LVCMOS33OD12': 161, + 'LVCMOS25OD12': 162, + 'LVCMOS18OD12': 163, + } + +# ADC +adc_attrids = { + 'EN': 0, + 'VCCX': 1, + 'IOVREF': 2, + 'VREF': 3, + 'USED_FLAG': 4, + } + +adc_attrvals = { + 'UNKNOWN': 0, + 'ENABLE': 1, + '3.3': 2, + '2.80': 3, + '2.55': 4, + '2.39': 5, + '2.23': 6, + '1.81': 7, + '1.65': 8, + '2.5': 9, + '2.12': 10, + '1.94': 11, + '1.69': 12, + '1.37': 13, + '1.25': 14, + '1.8': 15, + '1.53': 16, + '1.39': 17, + '1.30': 18, + '1.21': 19, + '0.99': 20, + '0.9': 21, + 'ON': 22 + } + + +# BSRAM +bsram_attrids = { + 'CEMUX_CEA': 0, + 'CEMUX_CEB': 1, + 'CLKMUX_CLKA': 2, + 'CLKMUX_CLKB': 3, + 'CSA2': 4, + 'CSA_0': 5, + 'CSA_1': 6, + 'CSA_2': 7, + 'CSB2': 8, + 'CSB_0': 9, + 'CSB_1': 10, + 'CSB_2': 11, + 'DBLWA': 12, + 'DBLWB': 13, + 'GSR': 14, + 'MODE': 15, + 'OUTREG_ASYNC': 16, + 'OUTREG_CEA': 17, + 'OUTREG_CEB': 18, + 'PORTB_IBEH': 19, + 'REGSET_RSTA': 20, + 'REGSET_RSTB': 21, + 'REGSET_WEB': 22, + 'SYNC': 23, + 'WEMUX_WEA': 24, + 'WEMUX_WEB': 25, + 'DPA_DATA_WIDTH': 26, + 'DPB_DATA_WIDTH': 27, + 'DPA_BEHB': 28, + 'DPA_BELB': 29, + 'DPA_MODE': 30, + 'DPA_REGMODE': 31, + 'DPB_BEHB': 32, + 'DPB_BELB': 33, + 'DPB_MODE': 34, + 'DPB_REGMODE': 35, + 'SDPA_DATA_WIDTH': 36, + 'SDPB_DATA_WIDTH': 37, + 'SDPA_BEHB': 38, + 'SDPA_BELB': 39, + 'SDPA_MODE': 40, + 'SDPA_REGMODE': 41, + 'SDPB_BEHB': 42, + 'SDPB_BELB': 43, + 'SDPB_MODE': 44, + 'SDPB_REGMODE': 45, + 'SPA_DATA_WIDTH': 46, + 'SPB_DATA_WIDTH': 47, + 'SPA_BEHB': 48, + 'SPA_BELB': 49, + 'SPB_BEHB': 50, + 'SPB_BELB': 51, + 'SPA_MODE': 52, + 'SPA_REG_MODE': 53, + 'SPB_MODE': 54, + 'SPB_REG_MODE': 55, + 'ROMA_DATA_WIDTH': 56, + 'ROMB_DATA_WIDTH': 57, + 'ROM_DATA_WIDTH': 58, + 'ROM_PORTA_BEHB': 59, + 'ROM_PORTA_BELB': 60, + 'ROM_PORTA_REGMODE':61, + 'ROM_PORTB_REGMODE':62, + 'PORTB_BELB': 63, + 'PORTA_MODE': 64, + 'PORTB_MODE': 65, + 'PORTB_BEHB': 66 + } + +bsram_attrvals = { + 'UNKNOWN': 0, + 'INV': 1, + 'ENABLE': 2, + 'SET': 3, + 'X36': 4, + '1': 5, + '2': 6, + '4': 7, + '9': 8, + '16': 9, + 'RBW': 10, + 'WT': 11, + 'OUTREG': 12, + 'DISABLE': 13, + 'RESET': 14 + } + +# slice +cls_attrids = { + 'MODE': 0, + 'REGMODE': 1, + 'SRMODE': 2, + 'GSR': 3, + 'LSRONMUX': 4, + 'CEMUX_1': 5, + 'CEMUX_CE': 6, + 'CLKMUX_1': 7, + 'CLKMUX_CLK': 8, + 'LSR_MUX_1': 9, + 'LSR_MUX_LSR': 10, + 'REG0_SD': 11, + 'REG1_SD': 12, + 'REG0_REGSET': 13, + 'REG1_REGSET': 14 + } + +cls_attrvals = { + 'UNKNOWN': 0, + '0': 1, + '1': 2, + 'SIG': 3, + 'INV': 4, + 'ENGSR': 5, + 'DISGSR': 6, + 'LSRMUX': 7, + 'LUT': 8, + 'LOGIC': 9, + 'ALU': 10, + 'SSRAM': 11, + 'FF': 12, + 'LATCH': 13, + 'ASYNC': 14, + 'LSR_OVER_CE': 15, + 'SET': 16, + 'RESET': 17 + } +# DLL +dll_attrids = { + 'CLKSEL': 0, + 'CODESCAL': 1, + 'CODESCALEN': 2, + 'DIVSEL': 3, + 'FORCE': 4, + 'GSR': 5, + 'ROSC': 6, + 'ROUNDOFF': 7, + 'RSTPOL': 8, + 'CLKMUX_SYSCLK': 9 + } + +dll_attrvals = { + 'UNKNOWN': 0, + 'HECLK0': 1, + 'HECLK1': 2, + 'HECLK2': 3, + 'HECLK3': 4, + 'SYSCLK': 5, + 'POS_22': 6, + 'POS_33': 7, + 'POS_44': 8, + 'NEG_11': 9, + 'NEG_22': 10, + 'NEG_33': 11, + 'NEG_44': 12, + 'ENABLE': 13, + 'FAST': 14, + 'DISABLE': 15, + 'NOINV': 16, + 'POS_11': 17, + 'INV': 18 + } + +# PLL +pll_attrids = { + 'BYPCK': 0, + 'BYPCKDIV': 1, + 'BYPCKPS': 2, + 'CLKOUTDIV3': 3, + 'CLKOUTDIV3SEL': 4, + 'CLKOUTDIV': 5, + 'CLKOUTDIVSEL': 6, + 'CLKOUTPS': 7, + 'CRIPPLE': 8, + 'DUTY': 9, + 'DUTYSEL': 10, + 'DPSEL': 11, + 'FBSEL': 12, + 'FDIV': 13, + 'FDIVSEL': 14, + 'FDLYPWD': 15, + 'FLDCOUNT': 16, + 'FLOCK': 17, + 'FLTOP': 18, + 'GMCGAIN': 19, + 'GMCMODE': 20, + 'GMCOUT': 21, + 'GMCVREF': 22, + 'ICPSEL': 23, + 'IDIV': 24, + 'IDIVSEL': 25, + 'INSEL': 26, + 'IRSTEN': 27, + 'KVCO': 28, + 'LPR': 29, + 'ODIV': 30, + 'ODIVSEL': 31, + 'OPDLY': 32, + 'OSDLY': 33, + 'PASEL': 34, + 'PDN': 35, + 'PHASE': 36, + 'PLOCK': 37, + 'PSDLY': 38, + 'PWDEN': 39, + 'RSTEN': 40, + 'RSTLF': 41, + 'SDIV': 42, + 'SELIN': 43, + 'SFTDLY': 44, + 'SRSTEN': 45, + 'CLKMUX_CLKIN2': 46, + 'CLKMUX_CLKIN1': 47, + 'CLKMUX_CLKFB0': 48, + 'PLLVCC0': 49, + 'PLLVCC0_BYPASS': 50, + 'PLLVCC0_TRIM0': 51, + 'PLLVCC0_TRIM1': 52, + 'PLLVCC1': 53, + 'PLLVCC1_BYPASS': 54, + 'PLLVCC1_TRIM0': 55, + 'PLLVCC1_TRIM1': 56, + 'VCOBIAS_EN_D': 57, + 'VCOBIAS_EN_U': 58, + 'DIVA': 59, + 'DIVB': 60, + 'DIVC': 61, + 'DIVD': 62, + 'DPAEN': 63, + 'DUTY_TRIM_A': 64, + 'DUTY_TRIM_B': 65, + 'ICPDYN_SEL': 66, + 'LPR_SEL': 67, + 'INTFB': 68, + 'MON': 69, + 'CKA': 70, + 'CKB': 71, + 'CKC': 72, + 'CKD': 73, + 'CKA_OUT': 74, + 'CKB_OUT': 75, + 'CKC_OUT': 76, + 'CKD_OUT': 77, + 'CKA_IN': 78, + 'CKB_IN': 79, + 'CKC_IN': 80, + 'CKD_IN': 81, + 'PSA_COARSE': 82, + 'PSB_COARSE': 83, + 'PSC_COARSE': 84, + 'PSD_COARSE': 85, + 'PSA_FINE': 86, + 'PSB_FINE': 87, + 'PSC_FINE': 88, + 'PSD_FINE': 89, + 'DTA_SEL': 90, + 'DTB_SEL': 91, + 'PSA_SEL': 92, + 'PSB_SEL': 93, + 'PSC_SEL': 94, + 'PSD_SEL': 95, + 'DIVA_SEL': 96, + 'DIVB_SEL': 97, + 'DIVC_SEL': 98, + 'DIVD_SEL': 99, + 'DTMS_ENA': 100, + 'DTMS_ENB': 101, + 'DTMS_ENC': 102, + 'DTMS_END': 103, + 'VCCREG_TRIM0': 104, + 'VCCREG_TRIM1': 105, + 'PLLREG0': 106, + } +pll_attrvals = { + 'UNKNOWN': 0, + 'BYPASS': 1, + 'DISABLE': 2, + 'ENABLE': 3, + 'CLKOUTPS': 4, + 'C1': 5, + 'C2': 6, + 'C3': 7, + 'DYN': 8, + 'PWD': 9, + 'CLKFB0': 10, + 'CLKFB1': 11, + 'CLKFB2': 12, + 'CLKFB3': 13, + 'CLKFB4': 14, + 'CLKFN0': 15, + 'FORCE0': 16, + 'FORCE1': 17, + 'CLKIN0': 18, + 'CLKIN1': 19, + 'CLKIN2': 20, + 'CLKIN3': 21, + 'CLKIN4': 22, + 'R1': 23, + 'R2': 24, + 'R3': 25, + 'R4': 26, + 'R5': 27, + 'R6': 28, + 'R7': 29, + 'RESET': 30, + 'INV': 31, + '0': 32, + 'P0': 33, + 'P50': 34, + 'P100': 35, + 'P200': 36, + 'M0': 37, + 'M50': 38, + 'M100': 39, + 'M200': 40, + 'CKB': 41, + 'CKC': 42, + 'CKD': 43, + 'VSO': 44, + 'CASCADE': 45, + 'ICLK': 46, + 'FCLK': 47, + 'CLKOUT': 48 + } diff --git a/apycula/chipdb.py b/apycula/chipdb.py index 5f888754..d62b2351 100644 --- a/apycula/chipdb.py +++ b/apycula/chipdb.py @@ -1,5 +1,6 @@ from dataclasses import dataclass, field from typing import Dict, List, Set, Tuple, Union, ByteString +from itertools import chain import re from functools import reduce from collections import namedtuple @@ -64,6 +65,10 @@ class Tile: for this specific tile type""" width: int height: int + # At the time of packing/unpacking the information about the types of cells + # is already lost, it is critical to work through the 'logicinfo' table so + # store it. + ttyp: int # a mapping from dest, source wire to bit coordinates pips: Dict[str, Dict[str, Set[Coord]]] = field(default_factory=dict) clock_pips: Dict[str, Dict[str, Set[Coord]]] = field(default_factory=dict) @@ -83,6 +88,15 @@ class Device: cmd_hdr: List[ByteString] = field(default_factory=list) cmd_ftr: List[ByteString] = field(default_factory=list) template: np.ndarray = None + # allowable values of bel attributes + # {table_name: [(attr_id, attr_value)]} + logicinfo: Dict[str, List[Tuple[int, int]]] = field(default_factory=dict) + # fuses for a pair of the "features" (or pairs of parameter values) + # {ttype: {table_name: {(feature_A, feature_B): {bits}}} + shortval: Dict[int, Dict[str, Dict[Tuple[int, int], Set[Coord]]]] = field(default_factory=dict) + # fuses for 16 of the "features" + # {ttype: {table_name: {(feature_0, feature_1, ..., feature_15): {bits}}} + longval: Dict[int, Dict[str, Dict[Tuple[int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int], Set[Coord]]]] = field(default_factory=dict) # always-connected dest, src aliases aliases: Dict[Tuple[int, int, str], Tuple[int, int, str]] = field(default_factory=dict) @@ -145,6 +159,17 @@ def fse_pips(fse, ttyp, table=2, wn=wirenames): return pips +# make PLL bels +def fse_pll(device, fse, ttyp): + bels = {} + # XXX use second grid in order to find PLL ttypes + if device in ['GW1N-1', 'GW1NZ-1']: + if ttyp == 89: + bel = bels.setdefault('RPLLB', Bel()) + else: + bel = bels.setdefault('RPLLA', Bel()) + return bels + # add the ALU mode # new_mode_bits: string like "0110000010011010" def add_alu_mode(base_mode, modes, lut, new_alu_mode, new_mode_bits): @@ -319,6 +344,79 @@ def set_banks(fse, db): for rd in fse[ttyp]['longval'][37]: db.grid[row][col].bels.setdefault(f"BANK{rd[0]}", Bel()) +_known_logic_tables = { + 8: 'DCS', + 9: 'GSR', + 10: 'IOLOGIC', + 11: 'IOB', + 12: 'SLICE', + 13: 'BSRAM', + 14: 'DSP', + 15: 'PLL', + 62: 'USB', + } + +_known_tables = { + 4: 'CONST', + 5: 'LUT', + 21: 'IOLOGICA', + 22: 'IOLOGICB', + 23: 'IOBA', + 24: 'IOBB', + 25: 'CLS0', + 26: 'CLS1', + 27: 'CLS2', + 28: 'CLS3', + 35: 'PLL', + 37: 'BANK', + 40: 'IOBC', + 41: 'IOBD', + 42: 'IOBE', + 43: 'IOBF', + 44: 'IOBG', + 45: 'IOBH', + 46: 'IOBI', + 47: 'IOBJ', + 53: 'DLLDEL0', + 54: 'DLLDEL1', + 56: 'DLL0', + 64: 'USB', + 66: 'EFLASH', + 68: 'ADC', + 80: 'DLL1', + } + +def fse_fill_logic_tables(dev, fse): + # logicinfo + for ltable in fse['header']['logicinfo'].keys(): + if ltable in _known_logic_tables.keys(): + table = dev.logicinfo.setdefault(_known_logic_tables[ltable], []) + else: + table = dev.logicinfo.setdefault(f"unknown_{ltable}", []) + for attr, val, _ in fse['header']['logicinfo'][ltable]: + table.append((attr, val)) + # shortval + ttypes = {t for row in fse['header']['grid'][61] for t in row} + for ttyp in ttypes: + if 'shortval' in fse[ttyp].keys(): + ttyp_rec = dev.shortval.setdefault(ttyp, {}) + for stable in fse[ttyp]['shortval'].keys(): + if stable in _known_tables: + table = ttyp_rec.setdefault(_known_tables[stable], {}) + else: + table = ttyp_rec.setdefault(f"unknown_{stable}", {}) + for f_a, f_b, *fuses in fse[ttyp]['shortval'][stable]: + table[(f_a, f_b)] = {fuse.fuse_lookup(fse, ttyp, f) for f in unpad(fuses)} + if 'longval' in fse[ttyp].keys(): + ttyp_rec = dev.longval.setdefault(ttyp, {}) + for ltable in fse[ttyp]['longval'].keys(): + if ltable in _known_tables: + table = ttyp_rec.setdefault(_known_tables[ltable], {}) + else: + table = ttyp_rec.setdefault(f"unknown_{ltable}", {}) + for f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, *fuses in fse[ttyp]['longval'][ltable]: + table[(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15)] = {fuse.fuse_lookup(fse, ttyp, f) for f in unpad(fuses)} + def from_fse(device, fse): dev = Device() ttypes = {t for row in fse['header']['grid'][61] for t in row} @@ -326,18 +424,65 @@ def from_fse(device, fse): for ttyp in ttypes: w = fse[ttyp]['width'] h = fse[ttyp]['height'] - tile = Tile(w, h) + tile = Tile(w, h, ttyp) tile.pips = fse_pips(fse, ttyp, 2, wirenames) tile.clock_pips = fse_pips(fse, ttyp, 38, clknames) if 5 in fse[ttyp]['shortval']: tile.bels = fse_luts(fse, ttyp) if 51 in fse[ttyp]['shortval']: tile.bels = fse_osc(device, fse, ttyp) + if ttyp in [88, 89]: + tile.bels = fse_pll(device, fse, ttyp) tiles[ttyp] = tile + fse_fill_logic_tables(dev, fse) dev.grid = [[tiles[ttyp] for ttyp in row] for row in fse['header']['grid'][61]] return dev +# get fuses for attr/val set using short/longval table +# returns a bit set +def get_table_fuses(attrs, table): + bits = set() + for key, fuses in table.items(): + # all 16 "features" must be present to be able to use a set of bits from the record + have_full_key = True + for attrval in key: + if attrval == 0: # no "feature" + continue + if attrval > 0: + # this "feature" must present + if attrval not in attrs: + have_full_key = False + break + continue + if attrval < 0: + # this "feature" is set by default and can only be unset + if abs(attrval) in attrs: + have_full_key = False + break + if not have_full_key: + continue + bits.update(fuses) + return bits + +# get fuses for attr/val set using shortval table for ttyp +# returns a bit set +def get_shortval_fuses(dev, ttyp, attrs, table_name): + return get_table_fuses(attrs, dev.shortval[ttyp][table_name]) + +# get fuses for attr/val set using longval table for ttyp +# returns a bit set +def get_longval_fuses(dev, ttyp, attrs, table_name): + return get_table_fuses(attrs, dev.longval[ttyp][table_name]) + +# add the attribute/value pair into an set, which is then passed to +# get_longval_fuses() and get_shortval_fuses() +def add_attr_val(dev, logic_table, attrs, attr, val): + for idx, attr_val in enumerate(dev.logicinfo[logic_table]): + if attr_val[0] == attr and attr_val[1] == val: + attrs.add(idx) + break + def get_pins(device): if device not in {"GW1N-1", "GW1NZ-1", "GW1N-4", "GW1N-9", "GW1NR-9", "GW1N-9C", "GW1NR-9C", "GW1NS-2", "GW1NS-2C", "GW1NS-4", "GW1NSR-4C"}: raise Exception(f"unsupported device {device}") @@ -425,6 +570,16 @@ def json_pinout(device): raise Exception("unsupported device") + +_pll_inputs = [(5, 'CLKFB'), (6, 'FBDSEL0'), (7, 'FBDSEL1'), (8, 'FBDSEL2'), (9, 'FBDSEL3'), + (10, 'FBDSEL4'), (11, 'FBDSEL5'), + (12, 'IDSEL0'), (13, 'IDSEL1'), (14, 'IDSEL2'), (15, 'IDSEL3'), (16, 'IDSEL4'), + (17, 'IDSEL5'), + (18, 'ODSEL0'), (19, 'ODSEL1'), (20, 'ODSEL2'), (21, 'ODSEL3'), (22, 'ODSEL4'), + (24, 'PSDA0'), (25, 'PSDA1'), (26, 'PSDA2'), (27, 'PSDA3'), + (28, 'DUTYDA0'), (29, 'DUTYDA1'), (30, 'DUTYDA2'), (31, 'DUTYDA3'), + (32, 'FDLY0'), (33, 'FDLY1'), (34, 'FDLY2'), (35, 'FDLY3')] +_pll_outputs = [(0, 'CLKOUT'), (1, 'LOCK'), (2, 'CLKOUTP'), (3, 'CLKOUTD'), (4, 'CLKOUTD3')] def dat_portmap(dat, dev): for row in dev.grid: for tile in row: @@ -447,6 +602,28 @@ def dat_portmap(dat, dev): bel.portmap['I'] = out oe = wirenames[dat[f'Iobuf{pin}OE']] bel.portmap['OE'] = oe + elif name.startswith("ODDR"): + d0 = wirenames[dat[f'Iologic{pin}In'][1]] + bel.portmap['D0'] = d0 + d1 = wirenames[dat[f'Iologic{pin}In'][2]] + bel.portmap['D1'] = d1 + tx = wirenames[dat[f'Iologic{pin}In'][27]] + bel.portmap['TX'] = tx + elif name == 'RPLLA': + for idx, nam in _pll_inputs: + wire = wirenames[dat['PllIn'][idx]] + bel.portmap[nam] = wire + for idx, nam in _pll_outputs: + wire = wirenames[dat['PllOut'][idx]] + bel.portmap[nam] = wire + bel.portmap['CLKIN'] = wirenames[124]; + elif name == 'RPLLB': + reset = wirenames[dat['PllIn'][0]] + bel.portmap['RESET'] = reset + reset_p = wirenames[dat['PllIn'][1]] + bel.portmap['RESET_P'] = reset_p + odsel5 = wirenames[dat['PllIn'][23]] + bel.portmap['ODSEL5'] = odsel5 def dat_aliases(dat, dev): for row in dev.grid: @@ -655,3 +832,4 @@ def loc2bank(db, row, col): bank = db.pin_bank[name + 'B'] return bank + diff --git a/apycula/gowin_pack.py b/apycula/gowin_pack.py index 44bfaac1..2c738b39 100644 --- a/apycula/gowin_pack.py +++ b/apycula/gowin_pack.py @@ -3,6 +3,7 @@ import re import pickle import itertools +import math import numpy as np import json import argparse @@ -10,9 +11,15 @@ from collections import namedtuple from apycula import codegen from apycula import chipdb +from apycula.chipdb import add_attr_val, get_shortval_fuses, get_longval_fuses +from apycula import attrids +from apycula.attrids import pll_attrids, pll_attrvals from apycula import bslib +from apycula import attrids from apycula.wirenames import wirenames, wirenumbers +device = "" + _verilog_name = re.compile(r"^[A-Za-z_0-9][A-Za-z_0-9$]*$") def sanitize_name(name): retname = name @@ -28,7 +35,7 @@ def sanitize_name(name): def get_bels(data): later = [] - belre = re.compile(r"R(\d+)C(\d+)_(?:GSR|SLICE|IOB|MUX2_LUT5|MUX2_LUT6|MUX2_LUT7|MUX2_LUT8|ODDR|OSC[ZFH]?|BUFS|RAMW)(\w*)") + belre = re.compile(r"R(\d+)C(\d+)_(?:GSR|SLICE|IOB|MUX2_LUT5|MUX2_LUT6|MUX2_LUT7|MUX2_LUT8|ODDR|OSC[ZFH]?|BUFS|RAMW|RPLL[AB])(\w*)") for cellname, cell in data['modules']['top']['cells'].items(): if cell['type'].startswith('DUMMY_') : continue @@ -68,6 +75,152 @@ def get_pips(data): def infovaluemap(infovalue, start=2): return {tuple(iv[:start]):iv[start:] for iv in infovalue} +# add the default pll attributes according to the documentation +_default_pll_inattrs = { + 'FCLKIN' : '100.00', + 'IDIV_SEL' : '0', + 'DYN_IDIV_SEL' : 'false', + 'FBDIV_SEL' : '00000000000000000000000000000010', # XXX not as in doc + 'DYN_FBDIV_SEL' : 'false', + 'ODIV_SEL' : '00000000000000000000000000001000', + 'DYN_ODIV_SEL' : 'false', + 'PSDA_SEL' : '0000 ', # XXX extra space for compatibility, but it will work with or without it in the future + 'DUTYDA_SEL' : '1000 ', # ^^^ + 'DYN_DA_EN' : 'false', + 'CLKOUT_FT_DIR' : '1', + 'CLKOUT_DLY_STEP': '00000000000000000000000000000000', + 'CLKOUTP_FT_DIR': '1', + 'CLKOUTP_DLY_STEP': '00000000000000000000000000000000', + 'DYN_SDIV_SEL' : '00000000000000000000000000000010', + 'CLKFB_SEL' : 'internal', + 'CLKOUTD_SRC' : 'CLKOUT', + 'CLKOUTD3_SRC' : 'CLKOUT', + 'CLKOUT_BYPASS' : 'false', + 'CLKOUTP_BYPASS': 'false', + 'CLKOUTD_BYPASS': 'false', + 'DEVICE' : 'GW1N-1' + + } + +def add_pll_default_attrs(attrs): + pll_inattrs = attrs.copy() + for k, v in _default_pll_inattrs.items(): + if k in pll_inattrs.keys(): + continue + pll_inattrs[k] = v + return pll_inattrs + +_default_pll_internal_attrs = { + 'INSEL': 'CLKIN1', + 'FBSEL': 'CLKFB3', + 'PLOCK': 'ENABLE', + 'FLOCK': 'ENABLE', + 'FLTOP': 'ENABLE', + 'GMCMODE': 15, + 'CLKOUTDIV3': 'ENABLE', + 'CLKOUTDIV': 'ENABLE', + 'CLKOUTPS': 'ENABLE', + 'PDN': 'ENABLE', + 'PASEL': 0, + 'IRSTEN': 'ENABLE', + 'SRSTEN': 'ENABLE', + 'PWDEN': 'ENABLE', + 'RSTEN': 'ENABLE', + 'FLDCOUNT': 16, + 'GMCGAIN': 0, + 'LPR': 'R4', + 'ICPSEL': 50, +} + +# typ - PLL type (RPLL, etc) +def set_pll_attrs(db, typ, attrs): + pll_inattrs = add_pll_default_attrs(attrs) + pll_attrs = _default_pll_internal_attrs.copy() + pll_attrs['IRSTEN'] = 'DISABLE' + pll_attrs['SRSTEN'] = 'DISABLE' + + if typ not in ['RPLL']: + raise Exception(f"PLL type {typ} is not supported for now") + + # parse attrs + for attr, val in pll_inattrs.items(): + # XXX clock in and feedback in + if attr == 'CLKOUTD_SRC': + if val == 'CLKOUTP': + pll_attrs['CLKOUTDIVSEL'] = 'CLKOUTPS' + continue + if attr == 'CLKOUTD3_SRC': + if val == 'CLKOUTP': + pll_attrs['CLKOUTDIV3SEL'] = 'CLKOUTPS' + continue + # XXX selin + if attr == 'DYN_IDIV_SEL': + if val == 'true': + pll_attrs['IDIVSEL'] = 'DYN' + continue + if attr == 'DYN_FBDIV_SEL': + if val == 'true': + pll_attrs['FDIVSEL'] = 'DYN' + continue + if attr == 'DYN_ODIV_SEL': + if val == 'true': + pll_attrs['ODIVSEL'] = 'DYN' + continue + if attr == 'CLKOUT_BYPASS': + if val == 'true': + pll_attrs['BYPCK'] = 'BYPASS' + continue + if attr == 'CLKOUTP_BYPASS': + if val == 'true': + pll_attrs['BYPCKPS'] = 'BYPASS' + continue + if attr == 'CLKOUTD_BYPASS': + if val == 'true': + pll_attrs['BYPCKDIV'] = 'BYPASS' + continue + if attr == 'IDIV_SEL': + idiv = 1 + int(val, 2) + pll_attrs['IDIV'] = idiv + continue + if attr == 'FBDIV_SEL': + fbdiv = 1 + int(val, 2) + pll_attrs['FDIV'] = fbdiv + continue + if attr == 'DYN_SDIV_SEL': + pll_attrs['SDIV'] = 2 + int(val, 2) + continue + if attr == 'ODIV_SEL': + odiv = int(val, 2) + pll_attrs['ODIV'] = odiv + continue + if attr == 'FCLKIN': + fclkin = float(val) + continue + + # XXX input is 24MHz only and output either 52MHz or 56MHz + # XXX input is 27MHz only and output either 58.5MHz or 63MHz + if device != "GW1N-1" and device != "GW1NZ-1": + raise Exception(f"PLL is not supported") + if (abs(fclkin - 24) > 0.01 and device == "GW1N-1") or (abs(fclkin - 27) > 0.01 and device == "GW1NZ-1"): + raise Exception(f"PLL input frequency {fclkin} is not supported") + if fbdiv == 13 and idiv == 6 and odiv == 8: + pll_attrs['FLDCOUNT'] = 16 + pll_attrs['ICPSEL'] = 20 + pll_attrs['LPR'] = 6 + elif fbdiv == 7 and idiv == 3 and odiv == 8: + pll_attrs['FLDCOUNT'] = 16 + pll_attrs['ICPSEL'] = 40 + pll_attrs['LPR'] = 5 + else: + raise Exception(f"PLL parameters are not supported for now") + + fin_attrs = set() + for attr, val in pll_attrs.items(): + if isinstance(val, str): + val = pll_attrvals[val] + add_attr_val(db, 'PLL', fin_attrs, pll_attrids[attr], val) + return fin_attrs + iostd_alias = { "HSTL18_II" : "HSTL18_I", "SSTL18_I" : "HSTL18_I", @@ -264,9 +417,16 @@ def place(db, tilemap, bels, cst, args): elif typ == "RAMW": bel = tiledata.bels['RAM16'] bits = bel.modes['0'] - print(bits) + #print(typ, bits) for r, c in bits: tile[r][c] = 1 + elif typ.startswith('RPLL'): + pll_attrs = set_pll_attrs(db, 'RPLL', parms) + bits = get_shortval_fuses(db, tiledata.ttyp, pll_attrs, 'PLL') + #print(typ, bits) + for r, c in bits: + tile[r][c] = 1 + else: print("unknown type", typ) @@ -359,6 +519,7 @@ def dualmode_pins(db, tilemap, args): tile[row][col] = 1 def main(): + global device pil_available = True try: from PIL import Image diff --git a/apycula/gowin_unpack.py b/apycula/gowin_unpack.py index 450bc174..82364f70 100644 --- a/apycula/gowin_unpack.py +++ b/apycula/gowin_unpack.py @@ -9,6 +9,7 @@ import importlib.resources from apycula import codegen from apycula import chipdb +from apycula.attrids import pll_attrids, pll_attrvals from apycula.bslib import read_bitstream from apycula.wirenames import wirenames @@ -39,9 +40,114 @@ def _io_mode_sort_func(mode): l += 1 return l +# +def get_attr_name(attrname_table, code): + for name, cod in attrname_table.items(): + if cod == code: + return name + return '' + +# fix names and types of the PLL attributes +# { internal_name: external_name } +_pll_attrs = { + 'IDIV' : 'IDIV_SEL', + 'IDIVSEL' : 'DYN_IDIV_SEL', + 'FDIV' : 'FBDIV_SEL', + 'FDIVSEL' : 'DYN_FBDIV_SEL', + 'ODIV' : 'ODIV_SEL', + 'ODIVSEL' : 'DYN_ODIV_SEL', + 'PHASE' : 'PSDA_SEL', + 'DUTY' : 'DUTYDA_SEL', + 'DPSEL' : 'DYN_DA_EN', + + 'OPDLY' : 'CLKOUT_DLY_STEP', + + 'OSDLY' : 'CLKOUTP_DLY_STEP', + 'SDIV' : 'DYN_SDIV_SEL', + + 'CLKOUTDIVSEL' : 'CLKOUTD_SRC', + 'CLKOUTDIV3SEL' : 'CLKOUTD3_SRC', + 'BYPCK' : 'CLKOUT_BYPASS', + 'BYPCKPS' : 'CLKOUTP_BYPASS', + 'BYPCKDIV' : 'CLKOUTD_BYPASS', + } +def pll_attrs_refine(in_attrs): + res = set() + for attr, val in in_attrs.items(): + if attr not in _pll_attrs.keys(): + continue + attr = _pll_attrs[attr] + new_val = f'"{val}"' + if attr in ['CLKOUTP_DLY_STEP', 'CLKOUT_DLY_STEP']: + new_val = val / 50 + elif attr in ['PSDA_SEL', 'DUTYDA_SEL']: + new_val = f'"{val:04b}"' + elif attr in ['IDIV_SEL', 'FBDIV_SEL']: + new_val = val - 1 + elif attr == 'DYN_SDIV_SEL': + new_val = val - 2 + elif attr == 'ODIV_SEL': + new_val = val + res.add(f'{attr}={new_val}') + return res + +# parse attributes and values use 'logicinfo' table +# returns {attr: value} +# attribute names are decoded with the attribute table, but the values are returned in raw form +def parse_attrvals(tile, logicinfo_table, fuse_table, attrname_table): + def is_neg_key(key): + for k in key: + if k < 0: + return True + return False + + def is_pos_key(key): + return not is_neg_key(key) + + res = {} + set_mask = set() + zero_mask = set() + # collect masks + for av, bits in fuse_table.items(): + if is_neg_key(av): + zero_mask.update(bits) + else: + set_mask.update(bits) + set_bits = {(row, col) for row, col in set_mask if tile[row][col] == 1} + zero_bits = {(row, col) for row, col in set_mask if tile[row][col] == 0} + # find candidates from fuse table + attrvals = set() + for raw_bits, test_fn in [(zero_bits, is_neg_key), (set_bits, is_pos_key)]: + cnd = { av: bits for av, bits in fuse_table.items() if test_fn(av) and bits.issubset(raw_bits)} + for av, bits in cnd.items(): + keep = True + for bt in cnd.values(): + if bits != bt and bits.issubset(bt): + keep = False + break + if keep: + attrvals.add(av) + + for key in attrvals: + for av in [a for a in key if a != 0]: + attr, val = logicinfo_table[av] + res[get_attr_name(attrname_table, attr)] = val + return res + +# { (row, col, type) : idx} +# type 'A'| 'B' +_pll_cells = {} + +# returns the A cell of the PLL +def get_pll_A(db, row, col, typ): + if typ == 'B': + col -= 1 + return row, col, 'A' + # noiostd --- this is the case when the function is called # with iostd by default, e.g. from the clock fuzzer # With normal gowin_unpack io standard is determined first and it is known. +# (bels, pips, clock_pips) def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True): # TLVDS takes two BUF bels, so skip the B bels. skip_bels = set() @@ -50,7 +156,15 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True) clock_pips = {} bels = {} for name, bel in tiledata.bels.items(): - if name[0:3] == "IOB": + if name.startswith("RPLL"): + idx = _pll_cells.setdefault(get_pll_A(db, row, col, name[4]), len(_pll_cells)) + attrvals = pll_attrs_refine(parse_attrvals(tile, db.logicinfo['PLL'], db.shortval[tiledata.ttyp]['PLL'], pll_attrids)) + modes = bels.setdefault(f'{name}{idx}', set()) + for attrval in attrvals: + modes.add(attrval) + print(idx, modes) + continue + if name.startswith("IOB"): #print(name) if noiostd: iostd = '' @@ -153,7 +267,6 @@ def parse_tile_(db, row, col, tile, default=True, noalias=False, noiostd = True) return {name: bel for name, bel in bels.items() if name not in skip_bels}, pips, clock_pips - dffmap = { "DFF": None, "DFFN": None, @@ -280,7 +393,7 @@ def tile2verilog(dbrow, dbcol, bels, pips, clock_pips, mod, cst, db): mod.wires.update({srcg, destg}) mod.assigns.append((destg, srcg)) - belre = re.compile(r"(IOB|LUT|DFF|BANK|CFG|ALU|RAM16|ODDR|OSC[ZFH]?|BUFS)(\w*)") + belre = re.compile(r"(IOB|LUT|DFF|BANK|CFG|ALU|RAM16|ODDR|OSC[ZFH]?|BUFS|RPLL[AB])(\w*)") if have_iologic(bels): bels_items = move_iologic(bels) else: @@ -307,6 +420,16 @@ def tile2verilog(dbrow, dbcol, bels, pips, clock_pips, mod, cst, db): mod.primitives[name] = lut cst.cells[name] = (row, col, int(idx) // 2, _sides[int(idx) % 2]) make_muxes(row, col, idx, db, mod) + elif typ.startswith("RPLL"): + name = f"PLL_{idx}" + pll = mod.primitives.setdefault(name, codegen.Primitive("rPLL", name)) + for paramval in flags: + param, _, val = paramval.partition('=') + pll.params[param] = val + portmap = db.grid[dbrow][dbcol].bels[bel[:-1]].portmap + for port, wname in portmap.items(): + pll.portmap[port] = f"R{row}C{col}_{wname}" + mod.wires.update(pll.portmap.values()) elif typ == "ALU": #print(flags) kind, = flags # ALU only have one flag diff --git a/examples/Makefile b/examples/Makefile index bec1d108..f0e2e39e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -21,7 +21,8 @@ all: attosoc-tec0117.fs nanolcd-tangnano.fs blinky-tec0117.fs blinky-runber.fs \ blinky-osc-tangnano9k.fs blinky-osc-tangnano1k.fs blinky-osc-tangnano4k.fs \ blinky-lw-tangnano.fs blinky-lw-tangnano1k.fs blinky-lw-tangnano4k.fs blinky-lw-tangnano9k.fs \ blinky-lw-honeycomb.fs blinky-lw-tec0117.fs blinky-lw-runber.fs \ - globals_all + globals_all \ + pll-tangnano.fs pll-tangnano1k.fs unpacked: attosoc-tec0117-unpacked.v nanolcd-tangnano-unpacked.v blinky-tec0117-unpacked.v blinky-runber-unpacked.v \ blinky-tangnano-unpacked.v blinky-honeycomb-unpacked.v shift-tec0117-unpacked.v shift-runber-unpacked.v \ @@ -114,10 +115,10 @@ nanolcd-tangnano-synth.json: nanolcd/TOP.v nanolcd/VGAMod.v $(YOSYS) -D LEDS_NR=8 -D OSC_TYPE_OSC -p "read_verilog $^; synth_gowin -json $@" %-tangnano-synth.json: %.v - $(YOSYS) -D LEDS_NR=3 -D OSC_TYPE_OSCH -p "read_verilog $^; synth_gowin -json $@" + $(YOSYS) -D LEDS_NR=3 -D OSC_TYPE_OSCH -p "read_verilog pll/GW1N-1.vh $^; synth_gowin -json $@" %-tangnano1k-synth.json: %.v - $(YOSYS) -D LEDS_NR=3 -D OSC_TYPE_OSCZ -p "read_verilog $^; synth_gowin -json $@" + $(YOSYS) -D LEDS_NR=3 -D OSC_TYPE_OSCZ -p "read_verilog pll/GW1NZ-1.vh $^; synth_gowin -json $@" %-tangnano4k-synth.json: %.v $(YOSYS) -D LEDS_NR=6 -D OSC_TYPE_OSCZ -p "read_verilog $^; synth_gowin -json $@" diff --git a/examples/pll.v b/examples/pll.v new file mode 100644 index 00000000..83a8b65b --- /dev/null +++ b/examples/pll.v @@ -0,0 +1,48 @@ +module top(input wire clk, output wire [`LEDS_NR-1:0]led); + wire VCC; + wire GND; + wire [2:0]dummy; + assign VCC = 1'b1; + assign GND = 1'b0; + rPLL pll( + .CLKOUT(led[0]), // connect an oscilloscope here + .CLKIN(clk), + .CLKOUTP(dummy[0]), + .CLKOUTD(dummy[1]), + .CLKOUTD3(dummy[2]), + .LOCK(led[1]), // this LED lights up when the PLL lock is triggered + .CLKFB(GND), + .FBDSEL({GND,GND,GND,GND,GND,GND}), + .IDSEL({GND,GND,GND,GND,GND,GND}), + .ODSEL({VCC,GND,GND,GND,GND,GND}), + .DUTYDA({GND,GND,GND,GND}), + .PSDA({GND,GND,GND,GND}), + .FDLY({GND,GND,GND,GND}), + .RESET(GND), + .RESET_P(GND) + ); + defparam pll.DEVICE = `PLL_DEVICE; + defparam pll.FCLKIN = `PLL_FCLKIN; + defparam pll.FBDIV_SEL = `PLL_FBDIV_SEL; + defparam pll.IDIV_SEL = `PLL_IDIV_SEL; + defparam pll.ODIV_SEL = `PLL_ODIV_SEL; + defparam pll.CLKFB_SEL="internal"; + defparam pll.CLKOUTD3_SRC="CLKOUT"; + defparam pll.CLKOUTD_BYPASS="false"; + defparam pll.CLKOUTD_SRC="CLKOUT"; + defparam pll.CLKOUTP_BYPASS="false"; + defparam pll.CLKOUTP_DLY_STEP=0; + defparam pll.CLKOUTP_FT_DIR=1'b1; + defparam pll.CLKOUT_BYPASS="false"; + defparam pll.CLKOUT_DLY_STEP=0; + defparam pll.CLKOUT_FT_DIR=1'b1; + defparam pll.DEVICE="GW1N-1"; + defparam pll.DUTYDA_SEL="1000"; + defparam pll.DYN_DA_EN="false"; + defparam pll.DYN_FBDIV_SEL="false"; + defparam pll.DYN_IDIV_SEL="false"; + defparam pll.DYN_ODIV_SEL="false"; + defparam pll.DYN_SDIV_SEL=2; + defparam pll.PSDA_SEL="0000"; +endmodule + diff --git a/examples/pll/GW1N-1.vh b/examples/pll/GW1N-1.vh new file mode 100644 index 00000000..cb32733b --- /dev/null +++ b/examples/pll/GW1N-1.vh @@ -0,0 +1,6 @@ +`define PLL_DEVICE "GW1N-1" +`define PLL_FCLKIN "24" +`define PLL_FBDIV_SEL 12 // 52 MHz (12, 5, 8) 56 MHz (6, 2, 8) +`define PLL_IDIV_SEL 5 +`define PLL_ODIV_SEL 8 + diff --git a/examples/pll/GW1NZ-1.vh b/examples/pll/GW1NZ-1.vh new file mode 100644 index 00000000..d571d6dc --- /dev/null +++ b/examples/pll/GW1NZ-1.vh @@ -0,0 +1,6 @@ +`define PLL_DEVICE "GW1NZ-1" +`define PLL_FCLKIN "27" +`define PLL_FBDIV_SEL 12 // 58.5 MHz (12, 5, 8) 63 MHz (6, 2, 8) +`define PLL_IDIV_SEL 5 +`define PLL_ODIV_SEL 8 +