efinix: add preliminary DDR support (WIP)
This commit is contained in:
parent
b9e99f576c
commit
24a920f2d1
|
@ -1,3 +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
|
|
@ -0,0 +1,97 @@
|
|||
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):
|
||||
self.blocks = []
|
||||
self.platform = platform
|
||||
self.config = config
|
||||
self.nb_ports = 1
|
||||
|
||||
if config['ports'] != None:
|
||||
self.nb_ports = self.config['ports']
|
||||
|
||||
self.clock_domains.cd_axi_ddr = ClockDomain()
|
||||
|
||||
self.port0 = port0 = axi.AXIInterface(data_width=256, address_width=32, id_width=8, clock_domain="axi_ddr")
|
||||
|
||||
if self.nb_ports == 2:
|
||||
self.port1 = port1 = axi.AXIInterface(data_width=256, address_width=32, id_width=8, clock_domain="axi_ddr")
|
||||
|
||||
axi_clk = platform.add_iface_io('axi_user_clk')
|
||||
self.cd_axi_ddr.clk.eq(axi_clk),
|
||||
|
||||
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 == 0:
|
||||
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 ??
|
||||
]
|
|
@ -188,6 +188,7 @@ def _build_peri(efinity_path, build_name, partnumber, named_sc, named_pc, fragme
|
|||
|
||||
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()
|
||||
|
@ -274,7 +275,7 @@ class EfinityToolchain():
|
|||
self.efinity_path = efinity_path
|
||||
self.additional_sdc_commands = []
|
||||
self.additional_xml_commands = []
|
||||
self.ifacewriter = InterfaceWriter("iface.py", efinity_path)
|
||||
self.ifacewriter = InterfaceWriter(efinity_path)
|
||||
self.specials_gpios = []
|
||||
self.additional_iface_commands = []
|
||||
|
||||
|
@ -338,6 +339,10 @@ class EfinityToolchain():
|
|||
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
|
||||
self.ifacewriter.add_ddr_xml(build_name)
|
||||
|
||||
# Run
|
||||
if run:
|
||||
subprocess.call([self.efinity_path + '/scripts/efx_run.py', build_name + '.xml', '-f', 'compile'])
|
||||
|
|
|
@ -1,12 +1,120 @@
|
|||
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, filename, efinity_path):
|
||||
self.file = filename
|
||||
def __init__(self, efinity_path):
|
||||
self.efinity_path = efinity_path
|
||||
self.blocks = []
|
||||
|
||||
def add_ddr_xml(self, filename):
|
||||
et.register_namespace('efxpt', "http://www.efinixinc.com/peri_design_db")
|
||||
tree = et.parse(filename + '.peri.xml')
|
||||
root = tree.getroot()
|
||||
ddr_info = root.find('efxpt:ddr_info', namespaces)
|
||||
|
||||
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_info, '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_0', is_bus = 'false', is_clk = 'true', is_clk_invert = 'false')
|
||||
|
||||
gen_pin_config = et.SubElement(ddr_info, '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_info, '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_info, '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_info, '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_info, '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_info, 'efxpt:cs_gate_delay')
|
||||
et.SubElement(cs_control, 'efxpt:param', name='EN_DLY_OVR', value= 'No', value_type='str')
|
||||
et.SubElement(cs_control, 'efxpt:param', name='GATE_C_DLY', value= '3', value_type='str')
|
||||
et.SubElement(cs_control, 'efxpt:param', name='GATE_F_DLY', value= '0', value_type='str')
|
||||
|
||||
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(filename), print_string)
|
||||
|
||||
def header(self, build_name, partnumber):
|
||||
header = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision()
|
||||
header += """
|
||||
|
@ -93,7 +201,7 @@ design.create('{2}', '{3}', './../build', overwrite=True)
|
|||
def footer(self):
|
||||
return """
|
||||
# Check design, generate constraints and reports
|
||||
design.generate(enable_bitstream=True)
|
||||
#design.generate(enable_bitstream=True)
|
||||
# Save the configured periphery design
|
||||
design.save()"""
|
||||
|
||||
|
|
|
@ -74,3 +74,10 @@ class EfinixPlatform(GenericPlatform):
|
|||
# We don't want this IO to be in the interface configuration file as a simple GPIO
|
||||
self.toolchain.specials_gpios.append(tmp)
|
||||
return tmp
|
||||
|
||||
def add_iface_ios(self, io):
|
||||
self.add_extension(io)
|
||||
tmp = self.request(io[0][0])
|
||||
for s in tmp.flatten():
|
||||
self.toolchain.specials_gpios.append(s)
|
||||
return tmp
|
||||
|
|
|
@ -79,11 +79,12 @@ class TRIONPLL(Module):
|
|||
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):
|
||||
def create_clkout(self, cd, freq, phase=0, margin=1e-2, with_reset=False, user_clk=True):
|
||||
assert self.nclkouts < self.nclkouts_max
|
||||
|
||||
clk_out_name = '{}_CLKOUT{}'.format(self.pll_name, self.nclkouts)
|
||||
|
||||
if user_clk == True:
|
||||
self.platform.add_extension([(clk_out_name, 0, Pins(1))])
|
||||
tmp = self.platform.request(clk_out_name)
|
||||
|
||||
|
@ -94,6 +95,7 @@ class TRIONPLL(Module):
|
|||
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)
|
||||
|
|
Loading…
Reference in New Issue