Merge pull request #1066 from fjullien/efinix

Initial Efinix support.
This commit is contained in:
enjoy-digital 2021-10-13 09:17:32 +02:00 committed by GitHub
commit eafa0fe83e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 2147 additions and 15 deletions

View File

@ -0,0 +1,4 @@
from litex.build.efinix.programmer import EfinixProgrammer
from litex.build.efinix.dbparser import EfinixDbParser
from litex.build.efinix.ifacewriter import InterfaceWriter
from litex.build.efinix.ddr import EfinixDDR

View File

@ -0,0 +1,43 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
from migen.fhdl.module import Module
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.build.io import *
# Efinix AsyncResetSynchronizer ---------------------------------------------------------------------
class EfinixAsyncResetSynchronizerImpl(Module):
def __init__(self, cd, async_reset):
rst1 = Signal()
self.specials += [
Instance("EFX_FF",
i_D = 0,
i_SR = async_reset,
i_CLK = cd.clk,
i_CE = 1,
o_Q = rst1),
Instance("EFX_FF",
i_D = rst1,
i_SR = async_reset,
i_CLK = cd.clk,
i_CE = 1,
o_Q = cd.rst)
]
class EfinixAsyncResetSynchronizer:
@staticmethod
def lower(dr):
return EfinixAsyncResetSynchronizerImpl(dr.cd, dr.async_reset)
# Gowin Special Overrides --------------------------------------------------------------------------
efinix_special_overrides = {
AsyncResetSynchronizer: EfinixAsyncResetSynchronizer
}

View File

@ -0,0 +1,99 @@
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:
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)
def get_gpio_instance_from_pin(self, pin):
dmap = self.get_device_map(self.device)
return self.get_pad_name_xml(dmap, pin)

93
litex/build/efinix/ddr.py Normal file
View File

@ -0,0 +1,93 @@
import os
from migen import *
from migen.genlib.cdc import *
from migen import ClockDomain
from litex.build.generic_platform import *
from litex.soc.interconnect import axi
from litex.build import tools
class EfinixDDR(Module):
def __init__(self, platform, config, cd):
self.blocks = []
self.platform = platform
self.config = config
self.nb_ports = 1
if config['ports'] != None:
self.nb_ports = self.config['ports']
# TODO: set clock_domain ?
self.port0 = port0 = axi.AXIInterface(data_width=256, address_width=32, id_width=8)
if self.nb_ports == 2:
self.port1 = port1 = axi.AXIInterface(data_width=256, address_width=32, id_width=8)
for i in range (0, self.nb_ports):
ios = [('axi', i,
Subsignal('wdata', Pins(256)),
Subsignal('wready', Pins(1)),
Subsignal('wid', Pins(8)),
Subsignal('bready', Pins(1)),
Subsignal('rdata', Pins(256)),
Subsignal('aid', Pins(8)),
Subsignal('bvalid', Pins(1)),
Subsignal('rlast', Pins(1)),
Subsignal('bid', Pins(8)),
Subsignal('asize', Pins(3)),
Subsignal('atype', Pins(1)),
Subsignal('aburst', Pins(2)),
Subsignal('wvalid', Pins(1)),
Subsignal('aaddr', Pins(32)),
Subsignal('rid', Pins(8)),
Subsignal('avalid', Pins(1)),
Subsignal('rvalid', Pins(1)),
Subsignal('alock', Pins(2)),
Subsignal('rready', Pins(1)),
Subsignal('rresp', Pins(2)),
Subsignal('wstrb', Pins(32)),
Subsignal('aready', Pins(1)),
Subsignal('alen', Pins(8)),
Subsignal('wlast', Pins(1)),
)]
io = platform.add_iface_ios(ios)
port = port0
if i == 1:
port = port1
is_read = port.ar.valid
self.comb += [io.aaddr.eq(Mux(is_read, port.ar.addr, port.aw.addr)),
io.aid.eq(Mux(is_read, port.ar.id, port.aw.id)),
io.alen.eq(Mux(is_read, port.ar.len, port.aw.len)),
io.asize.eq(Mux(is_read, port.ar.size[0:4], port.aw.size[0:4])), #TODO: check
io.aburst.eq(Mux(is_read, port.ar.burst, port.aw.burst)),
io.alock.eq(Mux(is_read, port.ar.lock, port.aw.lock)),
io.avalid.eq(Mux(is_read, port.ar.valid, port.aw.valid)),
io.atype.eq(~is_read),
port.aw.ready.eq(io.aready),
port.ar.ready.eq(io.aready),
io.wid.eq(port.w.id),
io.wstrb.eq(port.w.strb),
io.wdata.eq(port.w.data),
io.wlast.eq(port.w.last),
io.wvalid.eq(port.w.valid),
port.w.ready.eq(io.wready),
port.r.id.eq(io.rid),
port.r.data.eq(io.rdata),
port.r.last.eq(io.rlast),
port.r.resp.eq(io.rresp),
port.r.valid.eq(io.rvalid),
io.rready.eq(port.r.ready),
port.b.id.eq(io.bid),
port.b.valid.eq(io.bvalid),
io.bready.eq(port.b.ready),
# port.b.resp ??
]

View File

@ -0,0 +1,371 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import os
import subprocess
import pathlib
import math
import sys
import site
import subprocess
import inspect
import datetime
from xml.dom import expatbuilder
import xml.etree.ElementTree as et
from litex.build.generic_platform import *
from migen.fhdl.structure import _Fragment
from migen.fhdl.tools import *
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",
"defparam", "design", "disable", "edge", "else", "end", "endcase",
"endconfig", "endfunction", "endgenerate", "endmodule", "endprimitive",
"endspecify", "endtable", "endtask", "event", "for", "force", "forever",
"fork", "function", "generate", "genvar", "highz0", "highz1", "if",
"ifnone", "incdir", "include", "initial", "inout", "input",
"instance", "integer", "join", "large", "liblist", "library", "localparam",
"macromodule", "medium", "module", "nand", "negedge", "nmos", "nor",
"noshowcancelled", "not", "notif0", "notif1", "or", "output", "parameter",
"pmos", "posedge", "primitive", "pull0", "pull1" "pulldown",
"pullup", "pulsestyle_onevent", "pulsestyle_ondetect", "remos", "real",
"realtime", "reg", "release", "repeat", "rnmos", "rpmos", "rtran",
"rtranif0", "rtranif1", "scalared", "showcancelled", "signed", "small",
"specify", "specparam", "strong0", "strong1", "supply0", "supply1",
"table", "task", "time", "tran", "tranif0", "tranif1", "tri", "tri0",
"tri1", "triand", "trior", "trireg", "unsigned", "use", "vectored", "wait",
"wand", "weak0", "weak1", "while", "wire", "wor","xnor", "xor", "do"
}
def get_pin_direction(fragment, platform, pinname):
ios = platform.constraint_manager.get_io_signals()
sigs = list_signals(fragment) | list_special_ios(fragment, True, True, True)
special_outs = list_special_ios(fragment, False, True, True)
inouts = list_special_ios(fragment, False, False, True)
targets = list_targets(fragment) | special_outs
ns = build_namespace(list_signals(fragment) \
| list_special_ios(fragment, True, True, True) \
| ios, _reserved_keywords)
ns.clock_domains = fragment.clock_domains
dir = "Unknown"
for sig in sorted(ios, key=lambda x: x.duid):
# Better idea ???
if (pinname.split('[')[0] == ns.get_name(sig)):
if sig in inouts:
dir = "inout"
elif sig in targets:
dir = "output"
else:
dir = "input"
return dir
# Timing Constraints (.sdc) ------------------------------------------------------------------------
def _build_sdc(clocks, false_paths, vns, named_sc, build_name, additional_sdc_commands):
sdc = []
# Clock constraints
for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid):
is_port = False
for sig, pins, others, resname in named_sc:
if sig == vns.get_name(clk):
is_port = True
if is_port:
tpl = "create_clock -name {clk} -period {period} [get_ports {{{clk}}}]"
sdc.append(tpl.format(clk=vns.get_name(clk), period=str(period)))
else:
tpl = "create_clock -name {clk} -period {period} [get_nets {{{clk}}}]"
sdc.append(tpl.format(clk=vns.get_name(clk), period=str(period)))
# False path constraints
for from_, to in sorted(false_paths, key=lambda x: (x[0].duid, x[1].duid)):
tpl = "set_false_path -from [get_clocks {{{from_}}}] -to [get_clocks {{{to}}}]"
sdc.append(tpl.format(from_=vns.get_name(from_), to=vns.get_name(to)))
# Add additional commands
sdc += additional_sdc_commands
# Generate .sdc
tools.write_to_file("{}.sdc".format(build_name), "\n".join(sdc))
# Peripheral configuration ------------------------------------------------------------------------
def _create_gpio_instance(fragment, platform, sig, pins):
l = ""
if len(pins) > 1:
l = ',{},0'.format(len(pins) - 1)
d = get_pin_direction(fragment, platform, sig)
return 'design.create_{d}_gpio("{name}"{len})'.format(d=d, name=sig, len=l)
def _format_constraint(c, signame, fmt_r, fragment, platform):
# IO location constraints
if isinstance(c, Pins):
tpl = 'design.assign_pkg_pin("{signame}","{pin}")\n'
return tpl.format(signame=signame, name=fmt_r, pin=c.identifiers[0])
# IO standard property
elif isinstance(c, IOStandard):
prop = ""
valid = ['3.3_V_LVTTL_/_LVCMOS', '2.5_V_LVCMOS', '1.8_V_LVCMOS']
if c.name in valid:
prop = 'IO_STANDARD'
if prop == "":
print("{} has a wrong IOStandard format [{}]".format(signame, c.name))
print("Sould be selected from {}\n".format(valid))
# Print error, warning ??
return ""
tpl = 'design.set_property( "{signame}","{prop}","{val}")\n'
return tpl.format(signame=signame, prop=prop, val=c.name)
# Others constraints
elif isinstance(c, Misc):
prop = ""
if c.misc in ['WEAK_PULLUP', 'WEAK_PULLDOWN']:
prop = 'PULL_OPTION'
val = c.misc
if 'DRIVE_STRENGTH' in c.misc:
prop = 'DRIVE_STRENGTH'
val = c.misc.split('=')[1]
if prop == "":
# Print error, warning ??
return ""
tpl = 'design.set_property( "{signame}","{prop}","{val}")\n'
return tpl.format(signame=signame, prop=prop, val=val)
def _format_conf_constraint(signame, pin, others, resname, fragment, platform):
fmt_r = "{}:{}".format(*resname[:2])
if resname[2] is not None:
fmt_r += "." + resname[2]
fmt_c = [_format_constraint(c, signame, fmt_r, fragment, platform) for c in ([Pins(pin)] + others)]
return ''.join(fmt_c)
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))
else:
conf.append(_format_conf_constraint(sig, pins[0], others, resname, fragment, platform))
if named_pc:
conf.append("\n\n".join(named_pc))
conf = inst + conf
return "\n".join(conf)
def _build_peri(efinity_path, build_name, partnumber, named_sc, named_pc, fragment, platform, additional_iface_commands, specials_gpios):
pythonpath = ""
header = platform.toolchain.ifacewriter.header(build_name, partnumber)
gen = platform.toolchain.ifacewriter.generate()
#TODO: move this to ifacewriter
gpio = _build_iface_gpio(named_sc, named_pc, fragment, platform, specials_gpios)
add = '\n'.join(additional_iface_commands)
footer = platform.toolchain.ifacewriter.footer()
tools.write_to_file("iface.py", header + gen + gpio + add + footer)
subprocess.call([efinity_path + '/bin/python3', 'iface.py'])
# Project configuration ------------------------------------------------------------------------
def _build_xml(partnumber, build_name, sources, additional_xml_commands):
root = et.Element('efx:project')
now = datetime.datetime.now()
date_str = " Date: " + now.strftime("%Y-%m-%d %H:%M") + " "
# Add the required attributes
root.attrib['xmlns:efx'] = 'http://www.efinixinc.com/enf_proj'
root.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
root.attrib['name'] = build_name
root.attrib['description'] = ''
root.attrib['last_change_date'] = date_str
root.attrib['location'] = str(pathlib.Path().resolve())
root.attrib['sw_version'] = '2021.1.165.2.19' # TODO: read it from sw_version.txt
root.attrib['last_run_state'] = ''
root.attrib['last_run_tool'] = ''
root.attrib['last_run_flow'] = ''
root.attrib['config_result_in_sync'] = 'sync'
root.attrib['design_ood'] = 'sync'
root.attrib['place_ood'] = 'sync'
root.attrib['route_ood'] = 'sync'
root.attrib['xsi:schemaLocation'] = 'http://www.efinixinc.com/enf_proj enf_proj.xsd'
device_info = et.SubElement(root, 'efx:device_info')
et.SubElement(device_info, 'efx:family', name = 'Trion')
et.SubElement(device_info, 'efx:device', name = partnumber)
et.SubElement(device_info, 'efx:timing_model', name = 'C4')
design_info = et.SubElement(root, 'efx:design_info')
et.SubElement(design_info, "efx:top_module", name = build_name)
for filename, language, library in sources:
if '.vh' not in filename:
val = {'name':filename, 'version':'default', 'library':'default'}
et.SubElement(design_info, "efx:design_file", val)
et.SubElement(design_info, "efx:top_vhdl_arch", name = "")
constraint_info = et.SubElement(root, "efx:constraint_info")
et.SubElement(constraint_info, "efx:sdc_file", name = "{}.sdc".format(build_name))
misc_info = et.SubElement(root, "efx:misc_info")
ip_info = et.SubElement(root, "efx:ip_info")
synthesis = et.SubElement(root, "efx:synthesis", tool_name="efx_map")
for l in additional_xml_commands:
if l[0] == 'efx_map':
val = {'name':l[1], 'value':l[2], 'value_type':l[3]}
et.SubElement(synthesis, "efx:param", val)
place_and_route = et.SubElement(root, "efx:place_and_route", tool_name="efx_pnr")
for l in additional_xml_commands:
if l[0] == 'efx_pnr':
val = {'name':l[1], 'value':l[2], 'value_type':l[3]}
et.SubElement(place_and_route, "efx:param", val)
bitstream_generation = et.SubElement(root, "efx:bitstream_generation", tool_name="efx_pgm")
for l in additional_xml_commands:
if l[0] == 'efx_pgm':
val = {'name':l[1], 'value':l[2], 'value_type':l[3]}
et.SubElement(bitstream_generation, "efx:param", val)
xml_string = et.tostring(root, 'utf-8')
reparsed = expatbuilder.parseString(xml_string, False)
print_string = reparsed.toprettyxml(indent=" ")
# Generate .xml
tools.write_to_file("{}.xml".format(build_name), print_string)
class EfinityToolchain():
attr_translate = {}
def __init__(self, efinity_path):
self.options = {}
self.clocks = dict()
self.false_paths = set()
self.efinity_path = efinity_path
self.additional_sdc_commands = []
self.additional_xml_commands = []
self.ifacewriter = InterfaceWriter(efinity_path)
self.specials_gpios = []
self.additional_iface_commands = []
def build(self, platform, fragment,
build_dir = "build",
build_name = "top",
run = True,
**kwargs):
self.ifacewriter.set_build_params(platform, build_name)
# Create build directory
cwd = os.getcwd()
os.makedirs(build_dir, exist_ok=True)
os.chdir(build_dir)
# Finalize design
if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment()
platform.finalize(fragment)
# Generate verilog
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v"
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) + '}'
os.environ['EFXPT_HOME'] = self.efinity_path + '/pt'
# Generate design timing constraints file (.sdc)
_build_sdc(
clocks = self.clocks,
false_paths = self.false_paths,
vns = v_output.ns,
named_sc = named_sc,
build_name = build_name,
additional_sdc_commands = self.additional_sdc_commands)
# Generate project file (.xml)
_build_xml(
partnumber = platform.device,
build_name = build_name,
sources = platform.sources,
additional_xml_commands = self.additional_xml_commands)
# Generate constraints file (.peri.xml)
_build_peri(
efinity_path = self.efinity_path,
build_name = build_name,
partnumber = platform.device,
named_sc = named_sc,
named_pc = named_pc,
fragment = fragment,
platform = platform,
additional_iface_commands = self.additional_iface_commands,
specials_gpios = self.specials_gpios)
# DDR doesn't have Python API so we need to configure it
# directly in the peri.xml file
if self.ifacewriter.xml_blocks:
self.ifacewriter.generate_xml_blocks()
# Run
if run:
subprocess.call([self.efinity_path + '/scripts/efx_run.py', build_name + '.xml', '-f', 'compile'])
os.chdir(cwd)
return v_output.ns
def add_period_constraint(self, platform, clk, period):
clk.attr.add("keep")
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
if clk in self.clocks:
if period != self.clocks[clk]:
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
.format(self.clocks[clk], period))
self.clocks[clk] = period
def add_false_path_constraint(self, platform, from_, to):
from_.attr.add("keep")
to.attr.add("keep")
if (to, from_) not in self.false_paths:
self.false_paths.add((from_, to))

View File

@ -0,0 +1,367 @@
import os
import csv
import re
import datetime
from xml.dom import expatbuilder
import xml.etree.ElementTree as et
from litex.build import tools
namespaces = { 'efxpt' : 'http://www.efinixinc.com/peri_design_db',
'xi' : 'http://www.w3.org/2001/XInclude'
}
class InterfaceWriter():
def __init__(self, efinity_path):
self.efinity_path = efinity_path
self.blocks = []
self.xml_blocks = []
self.filename = ''
self.platform = None
def set_build_params(self, platform, build_name):
self.filename = build_name
self.platform = platform
def generate_xml_blocks(self):
et.register_namespace('efxpt', "http://www.efinixinc.com/peri_design_db")
tree = et.parse(self.filename + '.peri.xml')
root = tree.getroot()
for block in self.xml_blocks:
if block['type'] == 'DDR':
self.add_ddr_xml(root, block)
if block['type'] == 'LVDS':
self.add_ddr_lvds(root, block)
xml_string = et.tostring(root, 'utf-8')
reparsed = expatbuilder.parseString(xml_string, False)
print_string = reparsed.toprettyxml(indent=" ")
# Remove lines with only whitespaces. Not sure why they are here
print_string = os.linesep.join([s for s in print_string.splitlines() if s.strip()])
tools.write_to_file("{}.peri.xml".format(self.filename), print_string)
def add_ddr_lvds(self, root, params):
lvds_info = root.find('efxpt:lvds_info', namespaces)
if params['mode'] == 'OUTPUT':
dir = 'tx'
mode = 'out'
else:
dir = 'rx'
mode = 'in'
pad = self.platform.parser.get_gpio_instance_from_pin(params['location'][0])
pad = pad.replace('TXP', 'TX')
pad = pad.replace('TXN', 'TX')
pad = pad.replace('RXP', 'RX')
pad = pad.replace('RXN', 'RX')
# Sometimes there is an extra identifier at the end
# TODO: do a better parser
if pad.count('_') == 2:
pad = pad.rsplit('_', 1)[0]
lvds = et.SubElement(lvds_info, 'efxpt:lvds',
name = params['name'],
lvds_def = pad,
ops_type = dir)
et.SubElement(lvds, 'efxpt:ltx_info', pll_instance = '',
fast_clock_name = '{}'.format(params['fast_clk']),
slow_clock_name = '{}'.format(params['slow_clk']),
reset_name = '',
out_bname = '{}'.format(params['name']),
oe_name = '',
clock_div = '1',
mode = '{}'.format(mode),
serialization = '{}'.format(params['serialisation']),
reduced_swing = 'false',
load = '3')
def add_ddr_xml(self, root, params):
ddr_info = root.find('efxpt:ddr_info', namespaces)
ddr = et.SubElement(ddr_info, 'efxpt:ddr',
name = 'ddr_inst1',
ddr_def = 'DDR_0',
cs_preset_id = '173',
cs_mem_type = 'LPDDR3',
cs_ctrl_width = 'x32',
cs_dram_width = 'x32',
cs_dram_density = '8G',
cs_speedbin = '800',
target0_enable = 'true',
target1_enable = 'false',
ctrl_type = 'none')
axi_suffix = '' # '_1' for second port
type_suffix = '_0' # '_1' for second port
gen_pin_target0 = et.SubElement(ddr, 'efxpt:gen_pin_target0')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_wdata{}'.format(axi_suffix), type_name='WDATA{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_wready{}'.format(axi_suffix), type_name='WREADY{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_wid{}'.format(axi_suffix), type_name='WID{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_bready{}'.format(axi_suffix), type_name='BREADY{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_rdata{}'.format(axi_suffix), type_name='RDATA{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_aid{}'.format(axi_suffix), type_name='AID{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_bvalid{}'.format(axi_suffix), type_name='BVALID{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_rlast{}'.format(axi_suffix), type_name='RLAST{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_bid{}'.format(axi_suffix), type_name='BID{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_asize{}'.format(axi_suffix), type_name='ASIZE{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_atype{}'.format(axi_suffix), type_name='ATYPE{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_aburst{}'.format(axi_suffix), type_name='ABURST{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_wvalid{}'.format(axi_suffix), type_name='WVALID{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_wlast{}'.format(axi_suffix), type_name='WLAST{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_aaddr{}'.format(axi_suffix), type_name='AADDR{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_rid{}'.format(axi_suffix), type_name='RID{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_avalid{}'.format(axi_suffix), type_name='AVALID{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_rvalid{}'.format(axi_suffix), type_name='RVALID{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_alock{}'.format(axi_suffix), type_name='ALOCK{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_rready{}'.format(axi_suffix), type_name='RREADY{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_rresp{}'.format(axi_suffix), type_name='RRESP{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_wstrb{}'.format(axi_suffix), type_name='WSTRB{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_aready{}'.format(axi_suffix), type_name='AREADY{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_alen{}'.format(axi_suffix), type_name='ALEN{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target0, 'efxpt:pin', name='axi_clk', type_name='ACLK{}'.format(type_suffix), is_bus = 'false', is_clk = 'true', is_clk_invert = 'false')
axi_suffix = '_1' # '_1' for second port
type_suffix = '_1' # '_1' for second port
gen_pin_target1 = et.SubElement(ddr, 'efxpt:gen_pin_target1')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_wdata{}'.format(axi_suffix), type_name='WDATA{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_wready{}'.format(axi_suffix), type_name='WREADY{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_wid{}'.format(axi_suffix), type_name='WID{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_bready{}'.format(axi_suffix), type_name='BREADY{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_rdata{}'.format(axi_suffix), type_name='RDATA{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_aid{}'.format(axi_suffix), type_name='AID{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_bvalid{}'.format(axi_suffix), type_name='BVALID{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_rlast{}'.format(axi_suffix), type_name='RLAST{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_bid{}'.format(axi_suffix), type_name='BID{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_asize{}'.format(axi_suffix), type_name='ASIZE{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_atype{}'.format(axi_suffix), type_name='ATYPE{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_aburst{}'.format(axi_suffix), type_name='ABURST{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_wvalid{}'.format(axi_suffix), type_name='WVALID{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_wlast{}'.format(axi_suffix), type_name='WLAST{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_aaddr{}'.format(axi_suffix), type_name='AADDR{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_rid{}'.format(axi_suffix), type_name='RID{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_avalid{}'.format(axi_suffix), type_name='AVALID{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_rvalid{}'.format(axi_suffix), type_name='RVALID{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_alock{}'.format(axi_suffix), type_name='ALOCK{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_rready{}'.format(axi_suffix), type_name='RREADY{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_rresp{}'.format(axi_suffix), type_name='RRESP{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_wstrb{}'.format(axi_suffix), type_name='WSTRB{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_aready{}'.format(axi_suffix), type_name='AREADY{}'.format(type_suffix), is_bus = 'false')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_alen{}'.format(axi_suffix), type_name='ALEN{}'.format(type_suffix), is_bus = 'true')
et.SubElement(gen_pin_target1, 'efxpt:pin', name='axi_clk', type_name='ACLK{}'.format(type_suffix), is_bus = 'false', is_clk = 'true', is_clk_invert = 'false')
gen_pin_config = et.SubElement(ddr, 'efxpt:gen_pin_config')
et.SubElement(gen_pin_config, 'efxpt:pin', name='', type_name='CFG_SEQ_RST', is_bus = 'false')
et.SubElement(gen_pin_config, 'efxpt:pin', name='', type_name='CFG_SCL_IN', is_bus = 'false')
et.SubElement(gen_pin_config, 'efxpt:pin', name='', type_name='CFG_SEQ_START', is_bus = 'false')
et.SubElement(gen_pin_config, 'efxpt:pin', name='', type_name='RSTN', is_bus = 'false')
et.SubElement(gen_pin_config, 'efxpt:pin', name='', type_name='CFG_SDA_IN', is_bus = 'false')
et.SubElement(gen_pin_config, 'efxpt:pin', name='', type_name='CFG_SDA_OEN', is_bus = 'false')
cs_fpga = et.SubElement(ddr, 'efxpt:cs_fpga')
et.SubElement(cs_fpga, 'efxpt:param', name='FPGA_ITERM', value='120', value_type = 'str')
et.SubElement(cs_fpga, 'efxpt:param', name='FPGA_OTERM', value='34', value_type = 'str')
cs_memory = et.SubElement(ddr, 'efxpt:cs_memory')
et.SubElement(cs_memory, 'efxpt:param', name='RTT_NOM', value='RZQ/2', value_type = 'str')
et.SubElement(cs_memory, 'efxpt:param', name='MEM_OTERM', value='40', value_type = 'str')
et.SubElement(cs_memory, 'efxpt:param', name='CL', value='RL=6/WL=3', value_type = 'str')
timing = et.SubElement(ddr, 'efxpt:cs_memory_timing')
et.SubElement(timing, 'efxpt:param', name='tRAS', value= '42.000', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tRC', value= '60.000', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tRP', value= '18.000', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tRCD', value= '18.000', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tREFI', value= '3.900', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tRFC', value= '210.000', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tRTP', value= '10.000', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tWTR', value= '10.000', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tRRD', value= '10.000', value_type='float')
et.SubElement(timing, 'efxpt:param', name='tFAW', value= '50.000', value_type='float')
cs_control = et.SubElement(ddr, 'efxpt:cs_control')
et.SubElement(cs_control, 'efxpt:param', name='AMAP', value= 'ROW-COL_HIGH-BANK-COL_LOW', value_type='str')
et.SubElement(cs_control, 'efxpt:param', name='EN_AUTO_PWR_DN', value= 'Off', value_type='str')
et.SubElement(cs_control, 'efxpt:param', name='EN_AUTO_SELF_REF', value= 'No', value_type='str')
cs_gate_delay = et.SubElement(ddr, 'efxpt:cs_gate_delay')
et.SubElement(cs_gate_delay, 'efxpt:param', name='EN_DLY_OVR', value= 'No', value_type='str')
et.SubElement(cs_gate_delay, 'efxpt:param', name='GATE_C_DLY', value= '3', value_type='int')
et.SubElement(cs_gate_delay, 'efxpt:param', name='GATE_F_DLY', value= '0', value_type='int')
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}', './../gateware', 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_gpio(self, block, verbose=True):
name = block['name']
mode = block['mode']
cmd = ''
if mode == 'INOUT':
if len(block['location']) == 1:
cmd += 'design.create_inout_gpio("{}")\n'.format(name)
cmd += 'design.assign_pkg_pin("{}","{}")\n'.format(name, block['location'][0])
else:
cmd += 'design.create_inout_gpio("{}",{},0)\n'.format(name, block['size']-1)
for i, pad in enumerate(block['location']):
cmd += 'design.assign_pkg_pin("{}[{}]","{}")\n'.format(name, i, pad)
cmd += '\n'
return cmd
if mode == 'INPUT':
if len(block['location']) == 1:
cmd += 'design.create_input_gpio("{}")\n'.format(name)
cmd += 'design.assign_pkg_pin("{}","{}")\n'.format(name, block['location'][0])
else:
cmd += 'design.create_input_gpio("{}",{},0)\n'.format(name, block['size']-1)
for i, pad in enumerate(block['location']):
cmd += 'design.assign_pkg_pin("{}[{}]","{}")\n'.format(name, i, pad)
if 'in_reg' in block:
cmd += 'design.set_property("{}","IN_REG","{}")\n'.format(name, block['in_reg'])
cmd += 'design.set_property("{}","IN_CLK_PIN","{}")\n'.format(name, block['in_clk_pin'])
return cmd
if mode == 'OUTPUT':
if len(block['location']) == 1:
cmd += 'design.create_output_gpio("{}")\n'.format(name)
cmd += 'design.assign_pkg_pin("{}","{}")\n'.format(name, block['location'][0])
else:
cmd += 'design.create_input_gpio("{}",{},0)\n'.format(name, block['size']-1)
for i, pad in enumerate(block['location']):
cmd += 'design.assign_pkg_pin("{}[{}]","{}")\n'.format(name, i, pad)
if 'out_reg' in block:
cmd += 'design.set_property("{}","OUT_REG","{}")\n'.format(name, block['out_reg'])
cmd += 'design.set_property("{}","OUT_CLK_PIN","{}")\n'.format(name, block['out_clk_pin'])
if 'drive_strength' in block:
cmd += 'design.set_property("{}","DRIVE_STRENGTH","4")\n'.format(name, block['drive_strength'])
cmd += '\n'
return cmd
if mode == 'INPUT_CLK':
cmd += 'design.create_input_clock_gpio("{}")\n'.format(name)
cmd += 'design.set_property("{}","IN_PIN","{}")\n'.format(name, name)
cmd += 'design.assign_pkg_pin("{}","{}")\n\n'.format(name, block['location'])
return cmd
if mode == 'OUTPUT_CLK':
cmd += 'design.create_clockout_gpio("{}")\n'.format(name)
cmd += 'design.set_property("{}","OUT_CLK_PIN","{}")\n'.format(name, name)
cmd += 'design.assign_pkg_pin("{}","{}")\n\n'.format(name, block['location'])
return cmd
cmd = '# TODO: ' + str(block) +'\n'
return cmd
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 += 'pll_config = {{ "REFCLK_FREQ":"{}" }}\n'.format(block['input_freq'] / 1e6)
cmd += 'design.set_property("{}", pll_config, block_type="PLL")\n\n'.format(name)
if block['input_clock'] == 'EXTERNAL':
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'])
else:
cmd += 'design.gen_pll_ref_clock("{}", pll_res="{}", refclk_name="{}", refclk_src="CORE")\n'.format(name, block['resource'], block['input_signal'])
cmd += 'design.set_property("{}", "CORE_CLK_PIN", "{}", block_type="PLL")\n\n'.format(name, block['input_signal'])
cmd += 'design.set_property("{}","LOCKED_PIN","{}", block_type="PLL")\n'.format(name, block['locked'])
if block['reset'] != '':
cmd += 'design.set_property("{}","RSTN_PIN","{}", block_type="PLL")\n\n'.format(name, block['reset'])
# 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])
else:
cmd += 'pll_config = {{ "CLKOUT{}_PIN":"{}" }}\n'.format(i, clock[0])
cmd += 'design.set_property("{}", pll_config, block_type="PLL")\n\n'.format(name)
for i, clock in enumerate(block['clk_out']):
cmd += 'design.set_property("{}","CLKOUT{}_PHASE","{}","PLL")\n'.format(name, i, clock[2])
cmd += 'target_freq = {\n'
for i, clock in enumerate(block['clk_out']):
cmd += ' "CLKOUT{}_FREQ": "{}",\n'.format(i, clock[1] / 1e6)
cmd += '}\n'
cmd += 'calc_result = design.auto_calc_pll_clock("{}", target_freq)\n'.format(name)
if 'extra' in block:
cmd += block['extra']
cmd += '\n'
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", "CORE_CLK_PIN", "EXT_CLK", "CLKOUT1_EN", "CLKOUT2_EN","REFCLK_FREQ", "RESOURCE"]\n'
cmd += 'clock_source_prop += ["CLKOUT0_FREQ", "CLKOUT1_FREQ", "CLKOUT2_FREQ"]\n'
cmd += 'clock_source_prop += ["CLKOUT0_PHASE", "CLKOUT1_PHASE", "CLKOUT2_PHASE"]\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)
if b['type'] == 'GPIO':
output += self.generate_gpio(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()"""

View File

@ -0,0 +1,123 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
# Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import os
from litex.build.generic_platform import *
from litex.build.efinix import common, efinity
from litex.build.efinix import EfinixDbParser
# EfinixPlatform -----------------------------------------------------------------------------------
class EfinixPlatform(GenericPlatform):
bitstream_ext = ".bit"
def __init__(self, *args, toolchain="efinity", **kwargs):
GenericPlatform.__init__(self, *args, **kwargs)
self.pll_available = ['PLL_TL0', 'PLL_TR0', 'PLL_TR1', 'PLL_TR2', 'PLL_TR3', 'PLL_BR0', 'PLL_BR1', 'PLL_BR2', 'PLL_BL0']
self.pll_used = []
if 'LITEX_ENV_EFINITY' in os.environ:
self.efinity_path = os.environ['LITEX_ENV_EFINITY'].rstrip('/')
os.environ['EFINITY_HOME'] = self.efinity_path
else:
raise OSError('Unable to find Efinity toolchain, please set LITEX_ENV_EFINITY to ${install_dir}')
if toolchain == "efinity":
self.toolchain = efinity.EfinityToolchain(self.efinity_path)
else:
raise ValueError("Unknown toolchain")
self.parser = EfinixDbParser(self.efinity_path, self.device)
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
so = dict(common.efinix_special_overrides)
so.update(special_overrides)
return GenericPlatform.get_verilog(self, *args, special_overrides=so,
attr_translate=self.toolchain.attr_translate, **kwargs)
def build(self, *args, **kwargs):
return self.toolchain.build(self, *args, **kwargs)
def add_period_constraint(self, clk, period):
if clk is None: return
if hasattr(clk, "p"):
clk = clk.p
self.toolchain.add_period_constraint(self, clk, period)
def add_false_path_constraint(self, from_, to):
if hasattr(from_, "p"):
from_ = from_.p
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):
if sig is None:
return None
sc = self.constraint_manager.get_sig_constraints()
for s, pins, others, resource in sc:
if (s == sig) and (pins[0] != 'X'):
return pins
return None
def get_pin_name(self, sig):
if sig is None:
return None
sc = self.constraint_manager.get_sig_constraints()
for s, pins, others, resource in sc:
if s == sig:
if resource[2]:
return resource[0] + '_' + resource[2]
else:
return resource[0]
return None
def get_sig_constraint(self, sig):
sc = self.constraint_manager.get_sig_constraints()
for s, pins, others, resource in sc:
if s == sig:
return sc
return None
def add_iface_io(self, name, size=1, append=True):
self.add_extension([(name, 0, Pins(size))])
tmp = self.request(name)
# We don't want this IO to be in the interface configuration file as a simple GPIO
if append:
self.toolchain.specials_gpios.append(tmp)
return tmp
def add_iface_ios(self, io, append=True):
self.add_extension(io)
tmp = self.request(io[0][0])
if append:
for s in tmp.flatten():
self.toolchain.specials_gpios.append(s)
return tmp
def del_record_signal(self, record, sig):
for pos, (name, item) in enumerate(vars(record).items()):
if isinstance(item, Signal):
if item == sig:
# Two first pos are name and layout
del record.layout[pos-2]
delattr(record, name)
break
def get_pll_resource(self, name):
self.pll_used.append(name)
self.pll_available.remove(name)
print('Pll used : ' + str(self.pll_used))
print('Pll pll_available: ' + str(self.pll_available))
def get_free_pll_resource(self):
return self.pll_available[0]

View File

@ -0,0 +1,32 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
# SPDX-License-Identifier: BSD-2-Clause
import os
import sys
import subprocess
from litex.build.generic_programmer import GenericProgrammer
class EfinixProgrammer(GenericProgrammer):
def __init__(self, cable_name=""):
self.cable_name = cable_name
if 'LITEX_ENV_EFINITY' in os.environ:
self.efinity_path = os.environ['LITEX_ENV_EFINITY'].rstrip('/')
os.environ['EFINITY_HOME'] = self.efinity_path
else:
raise OSError('Unable to find Efinity toolchain, please set LITEX_ENV_EFINITY to ${install_dir}')
def load_bitstream(self, bitstream_file, cable_suffix=""):
os.environ['EFXPGM_HOME'] = self.efinity_path + '/pgm'
if (subprocess.call([self.efinity_path + '/bin/python3', self.efinity_path +
'/pgm/bin/efx_pgm/ftdi_program.py', bitstream_file,
"-m", "jtag"], env=os.environ.copy()) != 0):
msg = f"Error occured during {self.__class__.__name__}'s call, please check:\n"
msg += f"- {self.__class__.__name__} installation.\n"
msg += f"- access permissions.\n"
msg += f"- hardware and cable."
raise OSError(msg)

220
litex/build/efinix/rgmii.py Normal file
View File

@ -0,0 +1,220 @@
#
# This file is part of LiteEth.
#
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
# Copyright (c) 2015-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
# RGMII PHY for 7-Series Xilinx FPGA
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from litex.build.generic_platform import *
from litex.soc.cores.clock import *
from liteeth.common import *
from liteeth.phy.common import *
class LiteEthPHYRGMIITX(Module):
def __init__(self, platform, pads):
self.sink = sink = stream.Endpoint(eth_phy_description(8))
# # #
name = platform.get_pin_name(pads.tx_data)
pad = platform.get_pin_location(pads.tx_data)
name = 'auto_' + name
tx_data_h = []
tx_data_l = []
# This a workaround, we could use signals with 4 bits but there is
# a problem with the Python API that prevents it
for i in range(4):
tx_data_h.append(platform.add_iface_io(name + str(i) + '_HI'))
tx_data_l.append(platform.add_iface_io(name + str(i) + '_LO'))
block = {'type':'GPIO',
'mode':'OUTPUT',
'name':name + str(i),
'location':[pad[i]],
'size':1,
'out_reg':'DDIO_RESYNC',
'out_clk_pin':'auto_eth_tx_clk',
'is_inclk_inverted':False,
'drive_strength':4 # TODO: get this from pin constraints
}
platform.toolchain.ifacewriter.blocks.append(block)
platform.del_record_signal(pads, pads.tx_data)
name = platform.get_pin_name(pads.tx_ctl)
pad = platform.get_pin_location(pads.tx_ctl)
name = 'auto_' + name
tx_ctl_h = platform.add_iface_io(name + '_HI')
tx_ctl_l = platform.add_iface_io(name + '_LO')
block = {'type':'GPIO',
'mode':'OUTPUT',
'name':name,
'location':[pad[0]],
'size':1,
'out_reg':'DDIO_RESYNC',
'out_clk_pin':'auto_eth_tx_clk',
'is_inclk_inverted':False,
'drive_strength':4 # TODO: get this from pin constraints
}
platform.toolchain.ifacewriter.blocks.append(block)
platform.del_record_signal(pads, pads.tx_ctl)
self.sync += [ tx_data_h[0].eq(sink.data[0]),
tx_data_h[1].eq(sink.data[1]),
tx_data_h[2].eq(sink.data[2]),
tx_data_h[3].eq(sink.data[3]),
tx_data_l[0].eq(sink.data[4]),
tx_data_l[1].eq(sink.data[5]),
tx_data_l[2].eq(sink.data[6]),
tx_data_l[3].eq(sink.data[7]),
tx_ctl_h.eq(sink.valid),
tx_ctl_l.eq(sink.valid),
]
self.comb += sink.ready.eq(1)
class LiteEthPHYRGMIIRX(Module):
def __init__(self, platform, pads):
self.source = source = stream.Endpoint(eth_phy_description(8))
# # #
rx_data = Signal(8)
# Add a DDIO_RESYNC input block with 'auto_eth_rx_clk' as clock
name = platform.get_pin_name(pads.rx_data)
pad = platform.get_pin_location(pads.rx_data)
name = 'auto_' + name
rx_data_h = []
rx_data_l = []
# This a workaround, we could use signals with 4 bits but there is
# a problem with the Python API that prevents it
for i in range(4):
rx_data_h.append(platform.add_iface_io(name + str(i) + '_HI'))
rx_data_l.append(platform.add_iface_io(name + str(i) + '_LO'))
block = {'type':'GPIO',
'mode':'INPUT',
'name':name + str(i),
'location':[pad[i]],
'size':1,
'in_reg':'DDIO_RESYNC',
'in_clk_pin':'auto_eth_rx_clk',
'is_inclk_inverted':False
}
platform.toolchain.ifacewriter.blocks.append(block)
platform.del_record_signal(pads, pads.rx_data)
self.comb += rx_data.eq(Cat(rx_data_l[0], rx_data_l[1], rx_data_l[2], rx_data_l[3],
rx_data_h[0], rx_data_h[1], rx_data_h[2], rx_data_h[3]))
rx_ctl_d = Signal()
self.sync += rx_ctl_d.eq(pads.rx_ctl)
last = Signal()
self.comb += last.eq(~pads.rx_ctl & rx_ctl_d)
self.sync += [
source.valid.eq(rx_ctl_d),
source.data.eq(rx_data),
source.last.eq(last),
]
class LiteEthPHYRGMIICRG(Module, AutoCSR):
def __init__(self, platform, clock_pads, with_hw_init_reset, tx_delay=2e-9, hw_reset_cycles=256):
self._reset = CSRStorage()
# # #
# Clocks
self.clock_domains.cd_eth_rx = ClockDomain()
self.clock_domains.cd_eth_tx = ClockDomain()
# *************************
# * RX CLOCK *
# *************************
# Add a GPIO block with clock input property
# Add a input 'auto_eth_rx_clk' to the top.v
clkrx = platform.add_iface_io('auto_eth_rx_clk')
block = {'type':'GPIO',
'size':1,
# Get the location from the original resource
'location': platform.get_pin_location(clock_pads.rx)[0],
'name':platform.get_pin_name(clkrx),
'mode':'INPUT_CLK'
}
platform.toolchain.ifacewriter.blocks.append(block)
self.comb += self.cd_eth_rx.clk.eq(clkrx)
cmd = "create_clock -period {} auto_eth_rx_clk".format(1e9/125e6)
platform.toolchain.additional_sdc_commands.append(cmd)
# *************************
# * TX CLOCK PIN *
# *************************
block = {'type':'GPIO',
'size':1,
# Get the location from the original resource
'location': platform.get_pin_location(clock_pads.tx)[0],
'name':'auto_eth_tx_delayed_clk',
'mode':'OUTPUT_CLK'
}
platform.toolchain.ifacewriter.blocks.append(block)
# *************************
# * TX CLOCK *
# *************************
self.submodules.pll = pll = TRIONPLL(platform)
pll.register_clkin(None, 125e6, name='auto_eth_rx_clk')
pll.create_clkout(None, 125e6, phase=0, name='auto_eth_tx_delayed_clk')
pll.create_clkout(self.cd_eth_tx, 125e6, name='auto_eth_tx_clk')
cmd = "create_clock -period {} auto_eth_tx_clk".format(1e9/125e6)
platform.toolchain.additional_sdc_commands.append(cmd)
platform.delete(clock_pads)
# *************************
# * RESET *
# *************************
self.reset = reset = Signal()
if with_hw_init_reset:
self.submodules.hw_reset = LiteEthPHYHWReset(cycles=hw_reset_cycles)
self.comb += reset.eq(self._reset.storage | self.hw_reset.reset)
else:
self.comb += reset.eq(self._reset.storage)
if hasattr(clock_pads, "rst_n"):
self.comb += clock_pads.rst_n.eq(~reset)
self.specials += [
AsyncResetSynchronizer(self.cd_eth_tx, reset),
AsyncResetSynchronizer(self.cd_eth_rx, reset),
]
#platform.add_false_path_constraints(ClockSignal('sys'), self.cd_eth_rx.clk, self.cd_eth_tx.clk)
class LiteEthPHYRGMII(Module, AutoCSR):
dw = 8
tx_clk_freq = 125e6
rx_clk_freq = 125e6
def __init__(self, platform, clock_pads, pads, with_hw_init_reset=True, tx_delay=2e-9, rx_delay=2e-9,
iodelay_clk_freq=200e6, hw_reset_cycles=256):
self.submodules.crg = LiteEthPHYRGMIICRG(platform, clock_pads, with_hw_init_reset, tx_delay, hw_reset_cycles)
self.submodules.tx = ClockDomainsRenamer("eth_tx")(LiteEthPHYRGMIITX(platform, pads))
self.submodules.rx = ClockDomainsRenamer("eth_rx")(LiteEthPHYRGMIIRX(platform, pads))
self.sink, self.source = self.tx.sink, self.rx.source
#if hasattr(pads, "mdc"):
# self.submodules.mdio = LiteEthPHYMDIO(pads)

448
litex/build/efinix/video.py Normal file
View File

@ -0,0 +1,448 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2021 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import os
import math
from migen import *
from migen.genlib.cdc import MultiReg
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import stream
from litex.soc.cores.code_tmds import TMDSEncoder
from litex.build.io import SDROutput, DDROutput
# Video Constants ----------------------------------------------------------------------------------
hbits = 12
vbits = 12
# Video Timings ------------------------------------------------------------------------------------
video_timings = {
"640x480@60Hz" : {
"pix_clk" : 25.175e6,
"h_active" : 640,
"h_blanking" : 160,
"h_sync_offset" : 16,
"h_sync_width" : 96,
"v_active" : 480,
"v_blanking" : 45,
"v_sync_offset" : 10,
"v_sync_width" : 2,
},
"640x480@75Hz" : {
"pix_clk" : 31.5e6,
"h_active" : 640,
"h_blanking" : 200,
"h_sync_offset" : 16,
"h_sync_width" : 64,
"v_active" : 480,
"v_blanking" : 20,
"v_sync_offset" : 1,
"v_sync_width" : 3,
},
"800x600@60Hz" : {
"pix_clk" : 40e6,
"h_active" : 800,
"h_blanking" : 256,
"h_sync_offset" : 40,
"h_sync_width" : 128,
"v_active" : 600,
"v_blanking" : 28,
"v_sync_offset" : 1,
"v_sync_width" : 4,
},
"800x600@75Hz": {
"pix_clk" : 49.5e6,
"h_active" : 800,
"h_blanking" : 256,
"h_sync_offset" : 16,
"h_sync_width" : 80,
"v_active" : 600,
"v_blanking" : 25,
"v_sync_offset" : 1,
"v_sync_width" : 3,
},
"1024x768@60Hz": {
"pix_clk" : 65e6,
"h_active" : 1024,
"h_blanking" : 320,
"h_sync_offset" : 24,
"h_sync_width" : 136,
"v_active" : 768,
"v_blanking" : 38,
"v_sync_offset" : 3,
"v_sync_width" : 6,
},
"1024x768@75Hz": {
"pix_clk" : 78.8e6,
"h_active" : 1024,
"h_blanking" : 288,
"h_sync_offset" : 16,
"h_sync_width" : 96,
"v_active" : 768,
"v_blanking" : 32,
"v_sync_offset" : 1,
"v_sync_width" : 3,
},
"1280x720@60Hz": {
"pix_clk" : 74.25e6,
"h_active" : 1280,
"h_blanking" : 370,
"h_sync_offset" : 220,
"h_sync_width" : 40,
"v_active" : 720,
"v_blanking" : 30,
"v_sync_offset" : 5,
"v_sync_width" : 5,
},
"1920x1080@30Hz": {
"pix_clk" : 89.01e6,
"h_active" : 1920,
"h_blanking" : 720,
"h_sync_offset" : 528,
"h_sync_width" : 44,
"v_active" : 1080,
"v_blanking" : 45,
"v_sync_offset" : 4,
"v_sync_width" : 5,
},
"1920x1080@60Hz": {
"pix_clk" : 148.35e6,
"h_active" : 1920,
"h_blanking" : 280,
"h_sync_offset" : 88,
"h_sync_width" : 44,
"v_active" : 1080,
"v_blanking" : 45,
"v_sync_offset" : 4,
"v_sync_width" : 5,
},
"1920x1080@50Hz": {
"pix_clk" : 148.5e6,
"h_active" : 1920,
"h_blanking" : 720,
"h_sync_offset" : 528,
"h_sync_width" : 44,
"v_active" : 1080,
"v_blanking" : 45,
"v_sync_offset" : 4,
"v_sync_width" : 5,
},
"1920x1200@60Hz": {
"pix_clk" : 148.2e6,
"h_active" : 1920,
"h_blanking" : 80,
"h_sync_offset" : 8,
"h_sync_width" : 32,
"v_active" : 1200,
"v_blanking" : 35,
"v_sync_offset" : 21,
"v_sync_width" : 8,
},
}
# Video Timing Generator ---------------------------------------------------------------------------
video_timing_layout = [
# Synchronization signals.
("hsync", 1),
("vsync", 1),
("de", 1),
# Extended/Optional synchronization signals.
("hres", hbits),
("vres", vbits),
("hcount", hbits),
("vcount", vbits),
]
video_data_layout = [
# Synchronization signals.
("hsync", 1),
("vsync", 1),
("de", 1),
# Data signals.
("r", 8),
("g", 8),
("b", 8),
]
video_data_layout_dual = [
# Synchronization signals.
("hsync", 2),
("vsync", 2),
("de", 2),
# Data signals.
("r", 16),
("g", 16),
("b", 16),
]
class VideoTimingGenerator(Module, AutoCSR):
def __init__(self, default_video_timings="800x600@60Hz"):
# Check / Get Video Timings (can be str or dict)
if isinstance(default_video_timings, str):
try:
self.video_timings = vt = video_timings[default_video_timings]
except KeyError:
msg = [f"Video Timings {default_video_timings} not supported, availables:"]
for video_timing in video_timings.keys():
msg.append(f" - {video_timing} / {video_timings[video_timing]['pix_clk']/1e6:3.2f}MHz.")
raise ValueError("\n".join(msg))
else:
self.video_timings = vt = default_video_timings
# MMAP Control/Status Registers.
self._enable = CSRStorage(reset=1)
self._hres = CSRStorage(hbits, vt["h_active"])
self._hsync_start = CSRStorage(hbits, vt["h_active"] + vt["h_sync_offset"])
self._hsync_end = CSRStorage(hbits, vt["h_active"] + vt["h_sync_offset"] + vt["h_sync_width"])
self._hscan = CSRStorage(hbits, vt["h_active"] + vt["h_blanking"])
self._vres = CSRStorage(vbits, vt["v_active"])
self._vsync_start = CSRStorage(vbits, vt["v_active"] + vt["v_sync_offset"])
self._vsync_end = CSRStorage(vbits, vt["v_active"] + vt["v_sync_offset"] + vt["v_sync_width"])
self._vscan = CSRStorage(vbits, vt["v_active"] + vt["v_blanking"])
# Video Timing Source
self.source = source = stream.Endpoint(video_timing_layout)
# # #
# Resynchronize Enable to Video clock domain.
self.enable = enable = Signal()
self.specials += MultiReg(self._enable.storage, enable)
# Resynchronize Horizontal Timings to Video clock domain.
self.hres = hres = Signal(hbits)
self.hsync_start = hsync_start = Signal(hbits)
self.hsync_end = hsync_end = Signal(hbits)
self.hscan = hscan = Signal(hbits)
self.specials += MultiReg(self._hres.storage, hres)
self.specials += MultiReg(self._hsync_start.storage, hsync_start)
self.specials += MultiReg(self._hsync_end.storage, hsync_end)
self.specials += MultiReg(self._hscan.storage, hscan)
# Resynchronize Vertical Timings to Video clock domain.
self.vres = vres = Signal(vbits)
self.vsync_start = vsync_start = Signal(vbits)
self.vsync_end = vsync_end = Signal(vbits)
self.vscan = vscan = Signal(vbits)
self.specials += MultiReg(self._vres.storage, vres)
self.specials += MultiReg(self._vsync_start.storage, vsync_start)
self.specials += MultiReg(self._vsync_end.storage, vsync_end)
self.specials += MultiReg(self._vscan.storage, vscan)
# Generate timings.
hactive = Signal()
vactive = Signal()
fsm = FSM(reset_state="IDLE")
fsm = ResetInserter()(fsm)
self.submodules.fsm = fsm
self.comb += fsm.reset.eq(~enable)
fsm.act("IDLE",
NextValue(hactive, 0),
NextValue(vactive, 0),
NextValue(source.hres, hres),
NextValue(source.vres, vres),
NextValue(source.hcount, 0),
NextValue(source.vcount, 0),
NextState("RUN")
)
self.comb += source.de.eq(hactive & vactive) # DE when both HActive and VActive.
self.sync += source.first.eq((source.hcount == 0) & (source.vcount == 0)),
self.sync += source.last.eq( (source.hcount == hscan) & (source.vcount == vscan)),
fsm.act("RUN",
source.valid.eq(1),
If(source.ready,
# Increment HCount.
NextValue(source.hcount, source.hcount + 1),
# Generate HActive / HSync.
If(source.hcount == 0, NextValue(hactive, 1)), # Start of HActive.
If(source.hcount == hres, NextValue(hactive, 0)), # End of HActive.
If(source.hcount == hsync_start, NextValue(source.hsync, 1)), # Start of HSync.
If(source.hcount == hsync_end, NextValue(source.hsync, 0)), # End of HSync.
# End of HScan.
If(source.hcount == hscan,
# Reset HCount.
NextValue(source.hcount, 0),
# Increment VCount.
NextValue(source.vcount, source.vcount + 1),
# Generate VActive / VSync.
If(source.vcount == 0, NextValue(vactive, 1)), # Start of VActive.
If(source.vcount == vres, NextValue(vactive, 0)), # End of HActive.
If(source.vcount == vsync_start, NextValue(source.vsync, 1)), # Start of VSync.
If(source.vcount == vsync_end, NextValue(source.vsync, 0)), # End of VSync.
# End of VScan.
If(source.vcount == vscan,
# Reset VCount.
NextValue(source.vcount, 0),
)
)
)
)
# Video Patterns -----------------------------------------------------------------------------------
class ColorBarsPattern(Module):
"""Color Bars Pattern"""
def __init__(self):
self.enable = Signal(reset=1)
self.vtg_sink = vtg_sink = stream.Endpoint(video_timing_layout)
self.source = source = stream.Endpoint(video_data_layout)
# # #
enable = Signal()
self.specials += MultiReg(self.enable, enable)
# Control Path.
pix = Signal(hbits)
bar = Signal(3)
fsm = FSM(reset_state="IDLE")
fsm = ResetInserter()(fsm)
self.submodules.fsm = fsm
self.comb += fsm.reset.eq(~self.enable)
fsm.act("IDLE",
NextValue(pix, 0),
NextValue(bar, 0),
vtg_sink.ready.eq(1),
If(vtg_sink.valid & vtg_sink.first & (vtg_sink.hcount == 0) & (vtg_sink.vcount == 0),
vtg_sink.ready.eq(0),
NextState("RUN")
)
)
fsm.act("RUN",
vtg_sink.connect(source, keep={"valid", "ready", "last", "de", "hsync", "vsync"}),
If(source.valid & source.ready & source.de,
NextValue(pix, pix + 1),
If(pix == (vtg_sink.hres[3:] -1), # 8 Color Bars.
NextValue(pix, 0),
NextValue(bar, bar + 1)
)
)
)
# Data Path.
color_bar = [
# R G B
[0xff, 0xff, 0xff], # White
[0xff, 0xff, 0x00], # Yellow
[0x00, 0xff, 0xff], # Cyan
[0x00, 0xff, 0x00], # Green
[0xff, 0x00, 0xff], # Purple
[0xff, 0x00, 0x00], # Red
[0x00, 0x00, 0xff], # Blue
[0x00, 0x00, 0x00], # Black
]
cases = {}
for i in range(8):
cases[i] = [
source.r.eq(color_bar[i][0]),
source.g.eq(color_bar[i][1]),
source.b.eq(color_bar[i][2])
]
self.comb += Case(bar, cases)
class VideoLVDSPHY(Module):
def __init__(self, platform, pads, pixel_clk='px_clk', px_clk_x3_5='px_clk_x3_5'):
self.sink = sink = stream.Endpoint(video_data_layout_dual)
# # #
channel1 = Record(video_data_layout)
channel2 = Record(video_data_layout)
self.comb += [ channel1.r.eq(sink.r[0:8]),
channel1.g.eq(sink.g[0:8]),
channel1.b.eq(sink.b[0:8]),
channel1.de.eq(sink.de[0]),
channel1.vsync.eq(sink.vsync[0]),
channel1.hsync.eq(sink.hsync[0]),
channel2.r.eq(sink.r[7:16]),
channel2.g.eq(sink.g[7:16]),
channel2.b.eq(sink.b[7:16]),
channel2.de.eq(sink.de[1]),
channel2.vsync.eq(sink.vsync[1]),
channel2.hsync.eq(sink.hsync[1]),
]
# Always ack Sink, no backpressure.
self.comb += sink.ready.eq(1)
# -------- LVDS CLOCK -----------
name = platform.get_pin_name(pads.clk)
pad = platform.get_pin_location(pads.clk)
clk = platform.add_iface_io('hdmi_clk', 7)
block = {'type':'LVDS',
'mode':'OUTPUT',
'name':name,
'location':[pad[0]],
'serialisation':7,
'fast_clk':px_clk_x3_5,
'slow_clk':pixel_clk,
}
platform.toolchain.ifacewriter.xml_blocks.append(block)
platform.delete(pads.clk)
# -------- LVDS DATA -----------
lvds = []
lvds.append((pads.rxpa1, platform.get_pin_name(pads.rxpa1), platform.get_pin_location(pads.rxpa1)))
lvds.append((pads.rxpa2, platform.get_pin_name(pads.rxpa2), platform.get_pin_location(pads.rxpa2)))
rxpa1 = platform.add_iface_io('hdmi_rxpa1', 7)
rxpa2 = platform.add_iface_io('hdmi_rxpa2', 7)
lvds.append((pads.rxpb1, platform.get_pin_name(pads.rxpb1), platform.get_pin_location(pads.rxpb1)))
lvds.append((pads.rxpb2, platform.get_pin_name(pads.rxpb2), platform.get_pin_location(pads.rxpb2)))
rxpb1 = platform.add_iface_io('hdmi_rxpb1', 7)
rxpb2 = platform.add_iface_io('hdmi_rxpb2', 7)
lvds.append((pads.rxpc1, platform.get_pin_name(pads.rxpc1), platform.get_pin_location(pads.rxpc1)))
lvds.append((pads.rxpc2, platform.get_pin_name(pads.rxpc2), platform.get_pin_location(pads.rxpc2)))
rxpc1 = platform.add_iface_io('hdmi_rxpc1', 7)
rxpc2 = platform.add_iface_io('hdmi_rxpc2', 7)
lvds.append((pads.rxpd1, platform.get_pin_name(pads.rxpd1), platform.get_pin_location(pads.rxpd1)))
lvds.append((pads.rxpd2, platform.get_pin_name(pads.rxpd2), platform.get_pin_location(pads.rxpd2)))
rxpd1 = platform.add_iface_io('hdmi_rxpd1', 7)
rxpd2 = platform.add_iface_io('hdmi_rxpd2', 7)
for signal, name, pad in lvds:
block = {'type':'LVDS',
'mode':'OUTPUT',
'name':name,
'location':[pad[0]],
'serialisation':7,
'fast_clk':px_clk_x3_5,
'slow_clk':pixel_clk,
}
platform.toolchain.ifacewriter.xml_blocks.append(block)
platform.delete(signal)
self.comb += [ rxpa1.eq(Cat(channel1.g[2], channel1.r[7], channel1.r[6], channel1.r[5], channel1.r[4], channel1.r[3], channel1.r[2])),
rxpb1.eq(Cat(channel1.b[3], channel1.b[2], channel1.g[7], channel1.g[6], channel1.g[5], channel1.g[4], channel1.g[3])),
rxpc1.eq(Cat(channel1.de, channel1.vsync, channel1.hsync, channel1.b[7], channel1.b[6], channel1.b[5], channel1.b[4])),
rxpd1.eq(Cat(0, channel1.b[7], channel1.b[7], channel1.g[7], channel1.g[7], channel1.r[7], channel1.r[7])),
rxpa2.eq(Cat(channel2.g[2], channel2.r[7], channel2.r[6], channel2.r[5], channel2.r[4], channel2.r[3], channel2.r[2])),
rxpb2.eq(Cat(channel2.b[3], channel2.b[2], channel2.g[7], channel2.g[6], channel2.g[5], channel2.g[4], channel2.g[3])),
rxpc2.eq(Cat(channel2.de, channel2.vsync, channel2.hsync, channel2.b[7], channel2.b[6], channel2.b[5], channel2.b[4])),
rxpd2.eq(Cat(0, channel2.b[7], channel2.b[7], channel2.g[7], channel2.g[7], channel2.r[7], channel2.r[7])),
clk.eq(0b1100011),
]

View File

@ -190,6 +190,22 @@ class ConstraintManager:
def add_extension(self, io): def add_extension(self, io):
self.available.extend(io) self.available.extend(io)
def delete(self, signal):
for res, obj in self.matched:
if isinstance(obj, Record):
for pos, (name, item) in enumerate(vars(obj).items()):
if isinstance(item, Signal):
if item == signal:
# Two first pos are name and layout
del obj.layout[pos-2]
delattr(obj, name)
if len(obj.layout) == 0:
self.matched.remove((res, obj))
break
else:
if obj == signal:
self.matched.remove((res, obj))
def request(self, name, number=None, loose=False): def request(self, name, number=None, loose=False):
resource = _lookup(self.available, name, number, loose) resource = _lookup(self.available, name, number, loose)
if resource is None: if resource is None:
@ -275,6 +291,8 @@ class ConstraintManager:
if has_subsignals: if has_subsignals:
for element in resource[2:]: for element in resource[2:]:
if isinstance(element, Subsignal): if isinstance(element, Subsignal):
# Because we could have removed one Signal From the record
if hasattr(obj, element.name):
sig = getattr(obj, element.name) sig = getattr(obj, element.name)
pins, others = _separate_pins(top_constraints + pins, others = _separate_pins(top_constraints +
element.constraints) element.constraints)
@ -313,6 +331,9 @@ class GenericPlatform:
def request(self, *args, **kwargs): def request(self, *args, **kwargs):
return self.constraint_manager.request(*args, **kwargs) return self.constraint_manager.request(*args, **kwargs)
def delete(self, *args, **kwargs):
return self.constraint_manager.delete(*args, **kwargs)
def request_all(self, *args, **kwargs): def request_all(self, *args, **kwargs):
return self.constraint_manager.request_all(*args, **kwargs) return self.constraint_manager.request_all(*args, **kwargs)

175
litex/gen/fhdl/memory.py Normal file
View File

@ -0,0 +1,175 @@
from migen.fhdl.structure import *
from migen.fhdl.module import *
from migen.fhdl.bitcontainer import bits_for
from migen.fhdl.tools import *
from migen.fhdl.tracer import get_obj_var_name
from migen.fhdl.verilog import _printexpr as verilog_printexpr
from migen.fhdl.specials import Special, _MemoryPort, _MemoryLocation
(READ_FIRST, WRITE_FIRST, NO_CHANGE) = range(3)
class Memory(Special):
def __init__(self, width, depth, init=None, name=None):
Special.__init__(self)
self.width = width
self.depth = depth
self.ports = []
self.init = init
self.name_override = get_obj_var_name(name, "mem")
def __getitem__(self, index):
# simulation only
return _MemoryLocation(self, index)
def get_port(self, write_capable=False, async_read=False,
has_re=False, we_granularity=0, mode=WRITE_FIRST,
clock_domain="sys"):
if we_granularity >= self.width:
we_granularity = 0
adr = Signal(max=self.depth)
dat_r = Signal(self.width)
if write_capable:
if we_granularity:
we = Signal(self.width//we_granularity)
else:
we = Signal()
dat_w = Signal(self.width)
else:
we = None
dat_w = None
if has_re:
re = Signal()
else:
re = None
mp = _MemoryPort(adr, dat_r, we, dat_w,
async_read, re, we_granularity, mode,
clock_domain)
self.ports.append(mp)
return mp
@staticmethod
def emit_verilog(memory, ns, add_data_file):
r = ""
def gn(e):
if isinstance(e, Memory):
return ns.get_name(e)
else:
return verilog_printexpr(ns, e)[0]
adrbits = bits_for(memory.depth-1)
for i in range(memory.width // 8):
r += "reg [" + str((memory.width//4)-1) + ":0] " \
+ gn(memory) + '_' + str(i) \
+ "[0:" + str(memory.depth-1) + "];\n"
adr_regs = {}
data_regs = {}
for port in memory.ports:
if not port.async_read:
if port.mode == WRITE_FIRST:
adr_reg = Signal(name_override="memadr")
r += "reg [" + str(adrbits-1) + ":0] " \
+ gn(adr_reg) + ";\n"
adr_regs[id(port)] = adr_reg
else:
data_reg = Signal(name_override="memdat")
r += "reg [" + str(memory.width-1) + ":0] " \
+ gn(data_reg) + ";\n"
data_regs[id(port)] = data_reg
for port in memory.ports:
r += "always @(posedge " + gn(port.clock) + ") begin\n"
if port.we is not None:
if port.we_granularity:
n = memory.width//port.we_granularity
for i in range(n):
if (i > 0):
r += "always @(posedge " + gn(port.clock) + ") begin\n"
m = i*port.we_granularity
M = (i+1)*port.we_granularity-1
sl = "[" + str(M) + ":" + str(m) + "]"
r += "\tif (" + gn(port.we) + "[" + str(i) + "])\n"
r += "\t\t" + gn(memory) + '_' + str(i) + "[" + gn(port.adr) + "]" + " <= " + gn(port.dat_w) + sl + ";\n"
r += "end\n"
else:
r += "\tif (" + gn(port.we) + ")\n"
r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n"
if not port.async_read:
if port.mode == WRITE_FIRST:
r += "always @(posedge " + gn(port.clock) + ") begin\n"
rd = "\t" + gn(adr_regs[id(port)]) + " <= " + gn(port.adr) + ";\n"
else:
bassign = ""
for i in range(memory.width // 8):
m = i*port.we_granularity
M = (i+1)*port.we_granularity-1
sl = "[" + str(M) + ":" + str(m) + "]"
bassign += gn(data_regs[id(port)]) + sl + " <= " + gn(memory) + "_" + str(i) + "[" + gn(port.adr) + "];\n"
if port.mode == READ_FIRST:
rd = "\t" + bassign
elif port.mode == NO_CHANGE:
rd = "\tif (!" + gn(port.we) + ")\n" \
+ "\t\t" + bassign
if port.re is None:
r += rd
else:
r += "\tif (" + gn(port.re) + ")\n"
r += "\t" + rd.replace("\n\t", "\n\t\t")
r += "end\n\n"
for port in memory.ports:
if port.async_read:
r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n"
else:
if port.mode == WRITE_FIRST:
for i in range(memory.width // 8):
m = i*port.we_granularity
M = (i+1)*port.we_granularity-1
sl = "[" + str(M) + ":" + str(m) + "]"
r += "assign " + gn(port.dat_r) + sl + " = " + gn(memory) + "_" + str(i) + "[" + gn(adr_regs[id(port)]) + "];\n"
else:
r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n"
r += "\n"
if memory.init is not None:
content_7_0 = ""
content_15_8 = ""
content_23_16 = ""
content_31_24 = ""
formatter = "{:0" + str(int(memory.width / 4)) + "X}\n"
init_7_0 = []
init_15_8 = []
init_23_16 = []
init_31_24 = []
for w in memory.init:
init_7_0.append(w & 0xff)
init_15_8.append((w >> 8) & 0xff)
init_23_16.append((w >> 16) & 0xff)
init_31_24.append((w >> 24) & 0xff)
for d in init_7_0:
content_7_0 += formatter.format(d)
for d in init_15_8:
content_15_8 += formatter.format(d)
for d in init_23_16:
content_23_16 += formatter.format(d)
for d in init_31_24:
content_31_24 += formatter.format(d)
memory_filename1 = add_data_file(gn(memory) + "1.init", content_7_0)
memory_filename2 = add_data_file(gn(memory) + "2.init", content_15_8)
memory_filename3 = add_data_file(gn(memory) + "3.init", content_23_16)
memory_filename4 = add_data_file(gn(memory) + "4.init", content_31_24)
r += "initial begin\n"
r += "\t$readmemh(\"" + memory_filename1 + "\", " + gn(memory)+ "_0" + ");\n"
r += "\t$readmemh(\"" + memory_filename2 + "\", " + gn(memory)+ "_1" + ");\n"
r += "\t$readmemh(\"" + memory_filename3 + "\", " + gn(memory)+ "_2" + ");\n"
r += "\t$readmemh(\"" + memory_filename4 + "\", " + gn(memory)+ "_3" + ");\n"
r += "end\n\n"
return r

View File

@ -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_ice40 import iCE40PLL
from litex.soc.cores.clock.lattice_ecp5 import ECP5PLL from litex.soc.cores.clock.lattice_ecp5 import ECP5PLL
from litex.soc.cores.clock.lattice_nx import NXOSCA, NXPLL from litex.soc.cores.clock.lattice_nx import NXOSCA, NXPLL
# Efinix
from litex.soc.cores.clock.efinix_trion import TRIONPLL

View File

@ -0,0 +1,126 @@
#
# 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 *
class Open(Signal): pass
#TODO: do somthing else
count = 0
# Efinix / TRIONPLL ----------------------------------------------------------------------------------
class TRIONPLL(Module):
nclkouts_max = 4
def __init__(self, platform, with_reset=False):
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)
self.reset = Signal()
self.locked = Signal()
block = {}
count += 1
block['type'] = 'PLL'
block['name'] = self.pll_name
block['clk_out'] = []
pll_locked_name = self.pll_name + '_locked'
block['locked'] = pll_locked_name
io = self.platform.add_iface_io(pll_locked_name)
self.comb += self.locked.eq(io)
block['reset'] = ''
if with_reset:
pll_reset_name = self.pll_name + '_reset'
block['reset'] = pll_reset_name
io = self.platform.add_iface_io(pll_reset_name)
self.comb += io.eq(self.reset)
self.platform.toolchain.ifacewriter.blocks.append(block)
def register_clkin(self, clkin, freq, name= ''):
block = self.platform.toolchain.ifacewriter.get_block(self.pll_name)
block['input_clock_name'] = self.platform.get_pin_name(clkin)
# If clkin has a pin number, PLL clock input is EXTERNAL
if self.platform.get_pin_location(clkin):
pad_name = self.platform.get_pin_location(clkin)[0]
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))
try:
(pll_res, clock_no) = self.platform.parser.get_pll_inst_from_pin(pad_name)
except:
self.logger.error("Cannot find a pll with {} as input".format(pad_name))
quit()
block['input_clock'] = 'EXTERNAL'
block['resource'] = pll_res
block['clock_no'] = clock_no
self.logger.info("Clock source: {}, using EXT_CLK{}".format(block['input_clock'], clock_no))
self.platform.get_pll_resource(pll_res)
else:
block['input_clock'] = 'INTERNAL'
block['resource'] = self.platform.get_free_pll_resource()
block['input_signal'] = name
self.logger.info("Clock source: {}".format(block['input_clock']))
block['input_freq'] = freq
self.logger.info("Using {}".format(block['resource']))
def create_clkout(self, cd, freq, phase=0, margin=1e-2, name='', with_reset=False):
assert self.nclkouts < self.nclkouts_max
if name != '':
clk_out_name = name
else:
clk_out_name = '{}_CLKOUT{}'.format(self.pll_name, self.nclkouts)
if cd != None:
self.platform.add_extension([(clk_out_name, 0, Pins(1))])
tmp = self.platform.request(clk_out_name)
if with_reset:
self.specials += AsyncResetSynchronizer(cd, ~self.locked)
# 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 extra(self, extra):
block = self.platform.toolchain.ifacewriter.get_block(self.pll_name)
block['extra'] = extra
def compute_config(self):
pass
def set_configuration(self):
pass
def do_finalize(self):
pass

View File

@ -822,7 +822,7 @@ class SoC(Module):
self.check_if_exists(name) self.check_if_exists(name)
setattr(self.submodules, name, SoCController(**kwargs)) setattr(self.submodules, name, SoCController(**kwargs))
def add_ram(self, name, origin, size, contents=[], mode="rw"): def add_ram(self, name, origin, size, contents=[], mode="rw", no_we=False):
ram_cls = { ram_cls = {
"wishbone": wishbone.SRAM, "wishbone": wishbone.SRAM,
"axi-lite": axi.AXILiteSRAM, "axi-lite": axi.AXILiteSRAM,
@ -832,7 +832,7 @@ class SoC(Module):
"axi-lite": axi.AXILiteInterface, "axi-lite": axi.AXILiteInterface,
}[self.bus.standard] }[self.bus.standard]
ram_bus = interface_cls(data_width=self.bus.data_width) ram_bus = interface_cls(data_width=self.bus.data_width)
ram = ram_cls(size, bus=ram_bus, init=contents, read_only=(mode == "r")) ram = ram_cls(size, bus=ram_bus, init=contents, read_only=(mode == "r"), no_we=no_we)
self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode)) self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
self.check_if_exists(name) self.check_if_exists(name)
self.logger.info("RAM {} {} {}.".format( self.logger.info("RAM {} {} {}.".format(
@ -841,8 +841,8 @@ class SoC(Module):
self.bus.regions[name])) self.bus.regions[name]))
setattr(self.submodules, name, ram) setattr(self.submodules, name, ram)
def add_rom(self, name, origin, size, contents=[], mode="r"): def add_rom(self, name, origin, size, contents=[], mode="r", no_we=False):
self.add_ram(name, origin, size, contents, mode=mode) self.add_ram(name, origin, size, contents, mode=mode, no_we=no_we)
def init_rom(self, name, contents=[], auto_size=True): def init_rom(self, name, contents=[], auto_size=True):
self.logger.info("Initializing ROM {} with contents (Size: {}).".format( self.logger.info("Initializing ROM {} with contents (Size: {}).".format(

View File

@ -79,10 +79,12 @@ class SoCCore(LiteXSoC):
integrated_rom_size = 0, integrated_rom_size = 0,
integrated_rom_mode = "r", integrated_rom_mode = "r",
integrated_rom_init = [], integrated_rom_init = [],
integrated_rom_no_we = False,
# SRAM parameters # SRAM parameters
integrated_sram_size = 0x2000, integrated_sram_size = 0x2000,
integrated_sram_init = [], integrated_sram_init = [],
integrated_sram_no_we = False,
# MAIN_RAM parameters # MAIN_RAM parameters
integrated_main_ram_size = 0, integrated_main_ram_size = 0,
@ -197,11 +199,11 @@ class SoCCore(LiteXSoC):
# Add integrated ROM # Add integrated ROM
if integrated_rom_size: if integrated_rom_size:
self.add_rom("rom", self.cpu.reset_address, integrated_rom_size, integrated_rom_init, integrated_rom_mode) self.add_rom("rom", self.cpu.reset_address, integrated_rom_size, integrated_rom_init, integrated_rom_mode, no_we=integrated_rom_no_we)
# Add integrated SRAM # Add integrated SRAM
if integrated_sram_size: if integrated_sram_size:
self.add_ram("sram", self.mem_map["sram"], integrated_sram_size) self.add_ram("sram", self.mem_map["sram"], integrated_sram_size, no_we=integrated_sram_no_we)
# Add integrated MAIN_RAM (only useful when no external SRAM/SDRAM is available) # Add integrated MAIN_RAM (only useful when no external SRAM/SDRAM is available)
if integrated_main_ram_size: if integrated_main_ram_size:

View File

@ -783,7 +783,7 @@ class AXILite2CSR(Module):
# AXILite SRAM ------------------------------------------------------------------------------------- # AXILite SRAM -------------------------------------------------------------------------------------
class AXILiteSRAM(Module): class AXILiteSRAM(Module):
def __init__(self, mem_or_size, read_only=None, init=None, bus=None): def __init__(self, mem_or_size, read_only=None, init=None, bus=None, no_we=False):
if bus is None: if bus is None:
bus = AXILiteInterface() bus = AXILiteInterface()
self.bus = bus self.bus = bus

View File

@ -329,7 +329,7 @@ class Converter(Module):
# Wishbone SRAM ------------------------------------------------------------------------------------ # Wishbone SRAM ------------------------------------------------------------------------------------
class SRAM(Module): class SRAM(Module):
def __init__(self, mem_or_size, read_only=None, init=None, bus=None): def __init__(self, mem_or_size, read_only=None, init=None, bus=None, no_we=False):
if bus is None: if bus is None:
bus = Interface() bus = Interface()
self.bus = bus self.bus = bus
@ -337,8 +337,13 @@ class SRAM(Module):
if isinstance(mem_or_size, Memory): if isinstance(mem_or_size, Memory):
assert(mem_or_size.width <= bus_data_width) assert(mem_or_size.width <= bus_data_width)
self.mem = mem_or_size self.mem = mem_or_size
else:
if no_we:
from litex.gen.fhdl.memory import Memory as NoWeMemory
self.mem = NoWeMemory(bus_data_width, mem_or_size//(bus_data_width//8), init=init)
else: else:
self.mem = Memory(bus_data_width, mem_or_size//(bus_data_width//8), init=init) self.mem = Memory(bus_data_width, mem_or_size//(bus_data_width//8), init=init)
if read_only is None: if read_only is None:
if hasattr(self.mem, "bus_read_only"): if hasattr(self.mem, "bus_read_only"):
read_only = self.mem.bus_read_only read_only = self.mem.bus_read_only