efinix: support PLL, add dbparser and ifacewriter
This commit is contained in:
parent
0278d3eee8
commit
9b6ae2ff03
|
@ -1 +1,3 @@
|
|||
from litex.build.efinix.programmer import EfinixProgrammer
|
||||
from litex.build.efinix.dbparser import EfinixDbParser
|
||||
from litex.build.efinix.ifacewriter import InterfaceWriter
|
|
@ -0,0 +1,96 @@
|
|||
import os
|
||||
import csv
|
||||
import re
|
||||
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
namespaces = { 'efxpt' : 'http://www.efinixinc.com/peri_device_db',
|
||||
'xi' : 'http://www.w3.org/2001/XInclude'
|
||||
}
|
||||
|
||||
class EfinixDbParser():
|
||||
def __init__(self, efinity_path, device):
|
||||
self.efinity_db_path = efinity_path + '/pt/db/'
|
||||
self.device = device
|
||||
|
||||
def get_device_map(self, device):
|
||||
with open(self.efinity_db_path + 'devicemap.csv') as f:
|
||||
reader = csv.reader(f)
|
||||
data = list(reader)
|
||||
|
||||
for d in data:
|
||||
if d[0] == device:
|
||||
print(d)
|
||||
return d
|
||||
|
||||
return None
|
||||
|
||||
def get_package_file_name(self, dmap):
|
||||
tree = et.parse(self.efinity_db_path + dmap[2])
|
||||
root = tree.getroot()
|
||||
inc = root.findall('xi:include', namespaces)
|
||||
for i in inc:
|
||||
if 'package' in i.get('href'):
|
||||
return i.get('href').split('/')[1]
|
||||
|
||||
return None
|
||||
|
||||
def get_die_file_name(self, dmap):
|
||||
tree = et.parse(self.efinity_db_path + dmap[2])
|
||||
root = tree.getroot()
|
||||
inc = root.findall('xi:include', namespaces)
|
||||
for i in inc:
|
||||
if 'die' in i.get('href'):
|
||||
return i.get('href').split('/')[1]
|
||||
|
||||
return None
|
||||
|
||||
def get_pad_name_xml(self, dmap, pin):
|
||||
package_file = self.get_package_file_name(dmap)
|
||||
tree = et.parse(self.efinity_db_path + 'package/' + package_file)
|
||||
root = tree.getroot()
|
||||
|
||||
pm = root.findall('efxpt:package_map', namespaces)
|
||||
for p in pm:
|
||||
if p.get('package_pin') == pin:
|
||||
return (p.get('pad_name'))
|
||||
|
||||
return None
|
||||
|
||||
def get_instance_name_xml(self, dmap, pad):
|
||||
die = self.get_die_file_name(dmap)
|
||||
tree = et.parse(self.efinity_db_path + 'die/' + die)
|
||||
root = tree.getroot()
|
||||
|
||||
ipd = root.find('efxpt:io_pad_definition', namespaces)
|
||||
ios = ipd.findall('efxpt:io_pad_map', namespaces)
|
||||
for io in ios:
|
||||
if io.get('pad_name') == pad:
|
||||
return (io.get('instance'))
|
||||
|
||||
return None
|
||||
|
||||
def get_pll_inst_from_gpio_inst(self, dmap, inst):
|
||||
die = self.get_die_file_name(dmap)
|
||||
tree = et.parse(self.efinity_db_path + 'die/' + die)
|
||||
root = tree.getroot()
|
||||
|
||||
peri = root.findall('efxpt:periphery_instance', namespaces)
|
||||
for p in peri:
|
||||
if p.get('block') == 'pll':
|
||||
conn = p.findall('efxpt:single_conn', namespaces)
|
||||
for c in conn:
|
||||
if c.get('instance') == inst:
|
||||
refclk_no = 0
|
||||
if c.get('index') == '3':
|
||||
refclk_no = 1
|
||||
return (p.get('name'), refclk_no)
|
||||
|
||||
return None
|
||||
|
||||
def get_pll_inst_from_pin(self, pin):
|
||||
dmap = self.get_device_map(self.device)
|
||||
pad = self.get_pad_name_xml(dmap, pin)
|
||||
inst = self.get_instance_name_xml(dmap, pad)
|
||||
|
||||
return self.get_pll_inst_from_gpio_inst(dmap, inst)
|
|
@ -25,6 +25,8 @@ from migen.fhdl.namer import build_namespace
|
|||
from litex.build.generic_platform import Pins, IOStandard, Misc
|
||||
from litex.build import tools
|
||||
|
||||
from litex.build.efinix import InterfaceWriter
|
||||
|
||||
_reserved_keywords = {
|
||||
"always", "and", "assign", "automatic", "begin", "buf", "bufif0", "bufif1",
|
||||
"case", "casex", "casez", "cell", "cmos", "config", "deassign", "default",
|
||||
|
@ -157,13 +159,16 @@ def _format_conf_constraint(signame, pin, others, resname, fragment, platform):
|
|||
fmt_c = [_format_constraint(c, signame, fmt_r, fragment, platform) for c in ([Pins(pin)] + others)]
|
||||
return ''.join(fmt_c)
|
||||
|
||||
def _build_iface_conf(named_sc, named_pc, fragment, platform):
|
||||
def _build_iface_gpio(named_sc, named_pc, fragment, platform, specials_gpios):
|
||||
conf = []
|
||||
inst = []
|
||||
|
||||
# GPIO
|
||||
for sig, pins, others, resname in named_sc:
|
||||
if sig not in specials_gpios:
|
||||
inst.append(_create_gpio_instance(fragment, platform, sig, pins))
|
||||
else:
|
||||
continue
|
||||
if len(pins) > 1:
|
||||
for i, p in enumerate(pins):
|
||||
conf.append(_format_conf_constraint("{}[{}]".format(sig, i), p, others, resname, fragment, platform))
|
||||
|
@ -172,56 +177,20 @@ def _build_iface_conf(named_sc, named_pc, fragment, platform):
|
|||
if named_pc:
|
||||
conf.append("\n\n".join(named_pc))
|
||||
|
||||
# PLL
|
||||
#inst.append()
|
||||
|
||||
conf = inst + conf
|
||||
|
||||
return "\n".join(conf)
|
||||
|
||||
def _build_peri(efinity_path, build_name, partnumber, named_sc, named_pc, fragment, platform):
|
||||
def _build_peri(efinity_path, build_name, partnumber, named_sc, named_pc, fragment, platform, additional_iface_commands, specials_gpios):
|
||||
pythonpath = ""
|
||||
|
||||
header = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision()
|
||||
header = platform.toolchain.ifacewriter.header(build_name, partnumber)
|
||||
gen = platform.toolchain.ifacewriter.generate()
|
||||
gpio = _build_iface_gpio(named_sc, named_pc, fragment, platform, specials_gpios)
|
||||
add = '\n'.join(additional_iface_commands)
|
||||
footer = platform.toolchain.ifacewriter.footer()
|
||||
|
||||
header += """
|
||||
import os
|
||||
import sys
|
||||
|
||||
home = '{0}'
|
||||
|
||||
os.environ['EFXPT_HOME'] = home + '/pt'
|
||||
os.environ['EFXPGM_HOME'] = home + '/pgm'
|
||||
os.environ['EFXDBG_HOME'] = home + '/debugger'
|
||||
os.environ['EFXIPM_HOME'] = home + '/ipm'
|
||||
|
||||
sys.path.append(home + '/pt/bin')
|
||||
sys.path.append(home + '/lib/python3.8/site-packages')
|
||||
|
||||
from api_service.design import DesignAPI
|
||||
from api_service.device import DeviceAPI
|
||||
|
||||
is_verbose = {1}
|
||||
|
||||
design = DesignAPI(is_verbose)
|
||||
device = DeviceAPI(is_verbose)
|
||||
|
||||
design.create('{2}', '{3}', './../build', overwrite=True)
|
||||
|
||||
"""
|
||||
|
||||
header = header.format(efinity_path, 'True', build_name, partnumber)
|
||||
|
||||
conf = _build_iface_conf(named_sc, named_pc, fragment, platform)
|
||||
|
||||
footer = """
|
||||
# Check design, generate constraints and reports
|
||||
#design.generate(enable_bitstream=True)
|
||||
# Save the configured periphery design
|
||||
design.save()
|
||||
"""
|
||||
|
||||
tools.write_to_file("iface.py", header + conf + footer)
|
||||
tools.write_to_file("iface.py", header + gen + gpio + add + footer)
|
||||
|
||||
subprocess.call([efinity_path + '/bin/python3', 'iface.py'])
|
||||
|
||||
|
@ -295,6 +264,9 @@ class EfinityToolchain():
|
|||
self.efinity_path = efinity_path
|
||||
self.additional_sdc_commands = []
|
||||
self.additional_xml_commands = []
|
||||
self.ifacewriter = InterfaceWriter("iface.py", efinity_path)
|
||||
self.specials_gpios = []
|
||||
self.additional_iface_commands = []
|
||||
|
||||
def build(self, platform, fragment,
|
||||
build_dir = "build",
|
||||
|
@ -319,6 +291,9 @@ class EfinityToolchain():
|
|||
v_output.write(v_file)
|
||||
platform.add_source(v_file)
|
||||
|
||||
sc = platform.constraint_manager.get_sig_constraints()
|
||||
self.specials_gpios = [(v_output.ns.get_name(sig)) for sig in self.specials_gpios]
|
||||
|
||||
if platform.verilog_include_paths:
|
||||
self.options['includ_path'] = '{' + ';'.join(platform.verilog_include_paths) + '}'
|
||||
|
||||
|
@ -349,7 +324,9 @@ class EfinityToolchain():
|
|||
named_sc = named_sc,
|
||||
named_pc = named_pc,
|
||||
fragment = fragment,
|
||||
platform = platform)
|
||||
platform = platform,
|
||||
additional_iface_commands = self.additional_iface_commands,
|
||||
specials_gpios = self.specials_gpios)
|
||||
|
||||
# Run
|
||||
if run:
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
import os
|
||||
from litex.build import tools
|
||||
|
||||
class InterfaceWriter():
|
||||
def __init__(self, filename, efinity_path):
|
||||
self.file = filename
|
||||
self.efinity_path = efinity_path
|
||||
self.blocks = []
|
||||
|
||||
def header(self, build_name, partnumber):
|
||||
header = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision()
|
||||
header += """
|
||||
import os
|
||||
import sys
|
||||
import pprint
|
||||
|
||||
home = '{0}'
|
||||
|
||||
os.environ['EFXPT_HOME'] = home + '/pt'
|
||||
os.environ['EFXPGM_HOME'] = home + '/pgm'
|
||||
os.environ['EFXDBG_HOME'] = home + '/debugger'
|
||||
os.environ['EFXIPM_HOME'] = home + '/ipm'
|
||||
|
||||
sys.path.append(home + '/pt/bin')
|
||||
sys.path.append(home + '/lib/python3.8/site-packages')
|
||||
|
||||
from api_service.design import DesignAPI
|
||||
from api_service.device import DeviceAPI
|
||||
|
||||
is_verbose = {1}
|
||||
|
||||
design = DesignAPI(is_verbose)
|
||||
device = DeviceAPI(is_verbose)
|
||||
|
||||
design.create('{2}', '{3}', './../build', overwrite=True)
|
||||
|
||||
"""
|
||||
return header.format(self.efinity_path, 'True', build_name, partnumber)
|
||||
|
||||
def get_block(self, name):
|
||||
for b in self.blocks:
|
||||
if b['name'] == name:
|
||||
return b
|
||||
return None
|
||||
|
||||
def generate_pll(self, block, verbose=True):
|
||||
name = block['name']
|
||||
cmd = '# ---------- PLL {} ---------\n'.format(name)
|
||||
cmd += 'design.create_block("{}", block_type="PLL")\n'.format(name)
|
||||
cmd += 'design.gen_pll_ref_clock("{}", pll_res="{}", refclk_src="{}", refclk_name="{}", ext_refclk_no="{}")\n\n' \
|
||||
.format(name, block['resource'], block['input_clock'], block['input_clock_name'], block['clock_no'])
|
||||
|
||||
cmd += 'pll_config = {{ "REFCLK_FREQ":"{}" }}\n'.format(block['input_freq'] / 1e6)
|
||||
cmd += 'design.set_property("{}", pll_config, block_type="PLL")\n\n'.format(name)
|
||||
|
||||
# Output clock 0 is enabled by default
|
||||
for i, clock in enumerate(block['clk_out']):
|
||||
if i > 0:
|
||||
cmd += 'pll_config = {{ "CLKOUT{}_EN":"1", "CLKOUT{}_PIN":"{}" }}\n'.format(i, i, clock[0])
|
||||
cmd += 'design.set_property("{}", pll_config, block_type="PLL")\n\n'.format(name)
|
||||
|
||||
cmd += 'target_freq = {\n'
|
||||
for i, clock in enumerate(block['clk_out']):
|
||||
cmd += ' "CLKOUT{}_FREQ": "{}",\n'.format(i, clock[1] / 1e6)
|
||||
cmd += ' "CLKOUT{}_PHASE": "{}",\n'.format(i, clock[2])
|
||||
cmd += '}\n'
|
||||
cmd += 'calc_result = design.auto_calc_pll_clock("{}", target_freq)\n\n'.format(name)
|
||||
|
||||
|
||||
if verbose:
|
||||
cmd += 'print("#### {} ####")\n'.format(name)
|
||||
cmd += 'clksrc_info = design.trace_ref_clock("{}", block_type="PLL")\n'.format(name)
|
||||
cmd += 'pprint.pprint(clksrc_info)\n'
|
||||
cmd += 'clock_source_prop = ["REFCLK_SOURCE", "EXT_CLK", "CLKOUT0_EN", "CLKOUT1_EN","REFCLK_FREQ", "RESOURCE"]\n'
|
||||
cmd += 'clock_source_prop += ["M", "N", "O", "CLKOUT0_DIV", "CLKOUT2_DIV", "VCO_FREQ", "PLL_FREQ"]\n'
|
||||
cmd += 'prop_map = design.get_property("{}", clock_source_prop, block_type="PLL")\n'.format(name)
|
||||
cmd += 'pprint.pprint(prop_map)\n'
|
||||
|
||||
cmd += '# ---------- END PLL {} ---------\n\n'.format(name)
|
||||
return cmd
|
||||
|
||||
def generate(self):
|
||||
output = ''
|
||||
for b in self.blocks:
|
||||
if b['type'] == 'PLL':
|
||||
output += self.generate_pll(b)
|
||||
return output
|
||||
|
||||
def footer(self):
|
||||
return """
|
||||
# Check design, generate constraints and reports
|
||||
#design.generate(enable_bitstream=True)
|
||||
# Save the configured periphery design
|
||||
design.save()"""
|
||||
|
|
@ -50,3 +50,20 @@ class EfinixPlatform(GenericPlatform):
|
|||
if hasattr(to, "p"):
|
||||
to = to.p
|
||||
self.toolchain.add_false_path_constraint(self, from_, to)
|
||||
|
||||
# TODO: fix this when pin is like p = platform.request("sdios")
|
||||
# get_pin_location(p[1])
|
||||
# not tested with subsignal like get_pin_location(p.clk)
|
||||
def get_pin_location(self, sig):
|
||||
sc = self.constraint_manager.get_sig_constraints()
|
||||
for s, pins, others, resource in sc:
|
||||
if s == sig:
|
||||
return pins[0]
|
||||
return None
|
||||
|
||||
def get_pin_name(self, sig):
|
||||
sc = self.constraint_manager.get_sig_constraints()
|
||||
for s, pins, others, resource in sc:
|
||||
if s == sig:
|
||||
return resource[0]
|
||||
return None
|
|
@ -14,3 +14,6 @@ from litex.soc.cores.clock.intel_cyclone10 import Cyclone10LPPLL
|
|||
from litex.soc.cores.clock.lattice_ice40 import iCE40PLL
|
||||
from litex.soc.cores.clock.lattice_ecp5 import ECP5PLL
|
||||
from litex.soc.cores.clock.lattice_nx import NXOSCA, NXPLL
|
||||
|
||||
# Efinix
|
||||
from litex.soc.cores.clock.efinix_trion import TRIONPLL
|
|
@ -0,0 +1,92 @@
|
|||
#
|
||||
# This file is part of LiteX.
|
||||
#
|
||||
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
|
||||
# Copyright (c) 2021 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
from migen import *
|
||||
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||
|
||||
from litex.build.generic_platform import *
|
||||
from litex.soc.cores.clock.common import *
|
||||
|
||||
from litex.build.efinix import EfinixDbParser
|
||||
|
||||
class Open(Signal): pass
|
||||
|
||||
#TODO: do somthing else
|
||||
count = 0
|
||||
|
||||
# Efinix / TRIONPLL ----------------------------------------------------------------------------------
|
||||
|
||||
class TRIONPLL(Module):
|
||||
nclkouts_max = 4
|
||||
def __init__(self, platform):
|
||||
global count
|
||||
self.logger = logging.getLogger("TRIONPLL")
|
||||
self.logger.info("Creating TRIONPLL.".format())
|
||||
self.platform = platform
|
||||
self.nclkouts = 0
|
||||
self.pll_name = "pll{}".format(count)
|
||||
|
||||
block = {}
|
||||
count += 1
|
||||
|
||||
block['type'] = 'PLL'
|
||||
block['name'] = self.pll_name
|
||||
block['clk_out'] = []
|
||||
|
||||
self.platform.toolchain.ifacewriter.blocks.append(block)
|
||||
|
||||
def register_clkin(self, clkin, freq):
|
||||
block = self.platform.toolchain.ifacewriter.get_block(self.pll_name)
|
||||
|
||||
# If clkin has resource, PLL clock input is EXTERNAL
|
||||
# When PLL clock is external, it must not be present in the top file
|
||||
# Add a test on clkin resource here
|
||||
block['input_clock_name'] = self.platform.get_pin_name(clkin)
|
||||
pin_name = self.platform.get_pin_location(clkin)
|
||||
|
||||
self.platform.delete(clkin)
|
||||
|
||||
#tpl = "create_clock -name {clk} -period {period} [get_ports {{{clk}}}]"
|
||||
#sdc = self.platform.toolchain.additional_sdc_commands
|
||||
#sdc.append(tpl.format(clk=block['input_clock_name'], period=1/freq))
|
||||
|
||||
parser = EfinixDbParser(self.platform.efinity_path, self.platform.device)
|
||||
(pll_res, clock_no) = parser.get_pll_inst_from_pin(pin_name)
|
||||
|
||||
block['input_clock'] = 'EXTERNAL'
|
||||
block['input_freq'] = freq
|
||||
block['resource'] = pll_res
|
||||
block['clock_no'] = clock_no
|
||||
|
||||
self.logger.info("Using {}".format(pll_res))
|
||||
self.logger.info("Clock source: {}, using EXT_CLK{}".format(block['input_clock'], clock_no))
|
||||
|
||||
def create_clkout(self, cd, freq, phase=0, margin=1e-2, with_reset=True):
|
||||
assert self.nclkouts < self.nclkouts_max
|
||||
|
||||
clk_out_name = '{}_CLKOUT{}'.format(self.pll_name, self.nclkouts)
|
||||
|
||||
self.platform.add_extension([(clk_out_name, 0, Pins(1))])
|
||||
tmp = self.platform.request(clk_out_name)
|
||||
|
||||
# We don't want this IO to be in the interface configuration file as a simple GPIO
|
||||
self.platform.toolchain.specials_gpios.append(tmp)
|
||||
self.comb += cd.clk.eq(tmp)
|
||||
create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts)
|
||||
self.nclkouts += 1
|
||||
|
||||
block = self.platform.toolchain.ifacewriter.get_block(self.pll_name)
|
||||
block['clk_out'].append([clk_out_name, freq, phase, margin])
|
||||
|
||||
def compute_config(self):
|
||||
pass
|
||||
|
||||
def set_configuration(self):
|
||||
pass
|
||||
|
||||
def do_finalize(self):
|
||||
pass
|
Loading…
Reference in New Issue