Merge remote-tracking branch 'origin/master' into vexiiriscv
This commit is contained in:
commit
cb6e28aaa2
|
@ -54,9 +54,9 @@ The framework is also far from perfect and we'll be happy to have your [feedback
|
||||||
|
|
||||||
Have fun! :wink:
|
Have fun! :wink:
|
||||||
|
|
||||||
**Moral precisions**: The project is shared with a permissive BSD 2-Clause License and we are encouraged to continue sharing it this way thanks to the awesome community and clients willing to support the project!
|
We share this project under a permissive BSD 2-Clause License, inspired by our fantastic community and supportive clients. If LiteX benefits your research, hobby, or commercial projects, we kindly ask for your positive collaboration and respect for the effort involved.
|
||||||
If the projet is useful for your research, hobby or commercial projects, we are just asking you to be coherent and behave with integrity: Don't expect free support or that the community will be welcoming if your spent your time complaining about the project (and then direspect developers) or don't pay the custom developments you asked for... (While it's probably natural for 99% of users/clients, it does seems useful to add this for the 1% remaining that are eating lots of our energy/time).
|
|
||||||
|
|
||||||
|
Thank you for helping us improve LiteX and being part of our community!
|
||||||
|
|
||||||
# Typical LiteX design flow:
|
# Typical LiteX design flow:
|
||||||
```
|
```
|
||||||
|
|
|
@ -99,7 +99,6 @@ class AlteraDDROutputImpl(Module):
|
||||||
o_dataout = o,
|
o_dataout = o,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AlteraDDROutput:
|
class AlteraDDROutput:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def lower(dr):
|
def lower(dr):
|
||||||
|
@ -147,3 +146,103 @@ altera_special_overrides = {
|
||||||
SDROutput: AlteraSDROutput,
|
SDROutput: AlteraSDROutput,
|
||||||
SDRInput: AlteraSDRInput,
|
SDRInput: AlteraSDRInput,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Agilex5 DDROutput --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Agilex5DDROutputImpl(Module):
|
||||||
|
def __init__(self, i1, i2, o, clk):
|
||||||
|
self.specials += Instance("tennm_ph2_ddio_out",
|
||||||
|
p_mode = "MODE_DDR",
|
||||||
|
p_asclr_ena = "ASCLR_ENA_NONE",
|
||||||
|
p_sclr_ena = "SCLR_ENA_NONE",
|
||||||
|
o_dataout = o,
|
||||||
|
i_datainlo = i2,
|
||||||
|
i_datainhi = i1,
|
||||||
|
i_clk = clk,
|
||||||
|
i_ena = Constant(1, 1),
|
||||||
|
i_areset = Constant(1, 1),
|
||||||
|
i_sreset = Constant(1, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Agilex5DDROutput:
|
||||||
|
@staticmethod
|
||||||
|
def lower(dr):
|
||||||
|
return Agilex5DDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk)
|
||||||
|
|
||||||
|
# Agilex5 DDRInput ---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Agilex5DDRInputImpl(Module):
|
||||||
|
def __init__(self, i, o1, o2, clk):
|
||||||
|
self.specials += Instance("tennm_ph2_ddio_in",
|
||||||
|
p_mode = "MODE_DDR",
|
||||||
|
p_asclr_ena = "ASCLR_ENA_NONE",
|
||||||
|
p_sclr_ena = "SCLR_ENA_NONE",
|
||||||
|
i_clk = clk,
|
||||||
|
i_datain = i,
|
||||||
|
o_regouthi = o1,
|
||||||
|
o_regoutlo = o2,
|
||||||
|
i_ena = Constant(1, 1),
|
||||||
|
i_areset = Constant(1, 1),
|
||||||
|
i_sreset = Constant(1, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Agilex5DDRInput:
|
||||||
|
@staticmethod
|
||||||
|
def lower(dr):
|
||||||
|
return Agilex5DDRInputImpl(dr.i, dr.o1, dr.o2, dr.clk)
|
||||||
|
|
||||||
|
# Agilex5 SDROutput --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Agilex5SDROutput:
|
||||||
|
@staticmethod
|
||||||
|
def lower(dr):
|
||||||
|
return Agilex5DDROutputImpl(dr.i, dr.i, dr.o, dr.clk)
|
||||||
|
|
||||||
|
# Agilex5 SDRInput ---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Agilex5SDRInput:
|
||||||
|
@staticmethod
|
||||||
|
def lower(dr):
|
||||||
|
return Agilex5DDRInputImpl(dr.i, dr.o, Signal(), dr.clk)
|
||||||
|
|
||||||
|
# Agilex5 SDRTristate ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Agilex5SDRTristateImpl(Module):
|
||||||
|
def __init__(self, io, o, oe, i, clk):
|
||||||
|
_i = Signal()
|
||||||
|
_o = Signal()
|
||||||
|
_oe = Signal()
|
||||||
|
self.specials += [
|
||||||
|
SDRIO(o, _o, clk),
|
||||||
|
SDRIO(oe, _oe, clk),
|
||||||
|
SDRIO(_i, i, clk),
|
||||||
|
Instance("tennm_ph2_io_ibuf",
|
||||||
|
p_bus_hold = "BUS_HOLD_OFF",
|
||||||
|
io_i = io, # FIXME: its an input but io is needed to have correct dir at top module
|
||||||
|
o_o = _i,
|
||||||
|
),
|
||||||
|
Instance("tennm_ph2_io_obuf",
|
||||||
|
p_open_drain = "OPEN_DRAIN_OFF",
|
||||||
|
i_i = _o,
|
||||||
|
i_oe = _oe,
|
||||||
|
io_o = io, # FIXME: its an output but io is needed to have correct dir at top module
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
class Agilex5SDRTristate(Module):
|
||||||
|
@staticmethod
|
||||||
|
def lower(dr):
|
||||||
|
return Agilex5SDRTristateImpl(dr.io, dr.o, dr.oe, dr.i, dr.clk)
|
||||||
|
|
||||||
|
# Agilex5 Special Overrides ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
agilex5_special_overrides = {
|
||||||
|
AsyncResetSynchronizer: AlteraAsyncResetSynchronizer,
|
||||||
|
DifferentialInput: AlteraDifferentialInput,
|
||||||
|
DifferentialOutput: AlteraDifferentialOutput,
|
||||||
|
DDROutput: Agilex5DDROutput,
|
||||||
|
DDRInput: Agilex5DDRInput,
|
||||||
|
SDROutput: Agilex5SDROutput,
|
||||||
|
SDRInput: Agilex5SDRInput,
|
||||||
|
SDRTristate: Agilex5SDRTristate,
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ class AlteraPlatform(GenericPlatform):
|
||||||
|
|
||||||
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
|
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
|
||||||
so = dict(common.altera_special_overrides)
|
so = dict(common.altera_special_overrides)
|
||||||
|
if self.device[:3] == "A5E":
|
||||||
|
so.update(common.agilex5_special_overrides)
|
||||||
so.update(special_overrides)
|
so.update(special_overrides)
|
||||||
return GenericPlatform.get_verilog(self, *args,
|
return GenericPlatform.get_verilog(self, *args,
|
||||||
special_overrides = so,
|
special_overrides = so,
|
||||||
|
|
|
@ -162,21 +162,21 @@ class EfinixSDRTristate(Module):
|
||||||
|
|
||||||
class EfinixDifferentialOutputImpl(Module):
|
class EfinixDifferentialOutputImpl(Module):
|
||||||
def __init__(self, platform, i, o_p, o_n):
|
def __init__(self, platform, i, o_p, o_n):
|
||||||
# only keep _p
|
# only keep _p
|
||||||
io_name = platform.get_pin_name(o_p)
|
io_name = platform.get_pin_name(o_p)
|
||||||
io_pad = platform.get_pad_name(o_p) # need real pad name
|
io_pad = platform.get_pad_name(o_p) # need real pad name
|
||||||
io_prop = platform.get_pin_properties(o_p)
|
io_prop = platform.get_pin_properties(o_p)
|
||||||
|
|
||||||
if platform.family == "Titanium":
|
if platform.family == "Titanium":
|
||||||
# _p has _P_ and _n has _N_ followed by an optional function
|
# _p has _P_ and _n has _N_ followed by an optional function
|
||||||
# lvds block needs _PN_
|
# lvds block needs _PN_
|
||||||
pad_split = io_pad.split('_')
|
pad_split = io_pad.split('_')
|
||||||
assert pad_split[1] == 'P'
|
assert pad_split[1] == 'P'
|
||||||
io_pad = f"{pad_split[0]}_PN_{pad_split[2]}"
|
io_pad = f"{pad_split[0]}_PN_{pad_split[2]}"
|
||||||
else:
|
else:
|
||||||
assert "TXP" in io_pad
|
assert "TXP" in io_pad
|
||||||
# diff output pins are TXPYY and TXNYY
|
# diff output pins are TXPYY and TXNYY
|
||||||
# lvds block needs TXYY
|
# lvds block needs TXYY
|
||||||
io_pad = io_pad.replace("TXP", "TX")
|
io_pad = io_pad.replace("TXP", "TX")
|
||||||
|
|
||||||
platform.add_extension([(io_name, 0, Pins(1))])
|
platform.add_extension([(io_name, 0, Pins(1))])
|
||||||
|
@ -206,21 +206,21 @@ class EfinixDifferentialOutput:
|
||||||
|
|
||||||
class EfinixDifferentialInputImpl(Module):
|
class EfinixDifferentialInputImpl(Module):
|
||||||
def __init__(self, platform, i_p, i_n, o):
|
def __init__(self, platform, i_p, i_n, o):
|
||||||
# only keep _p
|
# only keep _p
|
||||||
io_name = platform.get_pin_name(i_p)
|
io_name = platform.get_pin_name(i_p)
|
||||||
io_pad = platform.get_pad_name(i_p) # need real pad name
|
io_pad = platform.get_pad_name(i_p) # need real pad name
|
||||||
io_prop = platform.get_pin_properties(i_p)
|
io_prop = platform.get_pin_properties(i_p)
|
||||||
|
|
||||||
if platform.family == "Titanium":
|
if platform.family == "Titanium":
|
||||||
# _p has _P_ and _n has _N_ followed by an optional function
|
# _p has _P_ and _n has _N_ followed by an optional function
|
||||||
# lvds block needs _PN_
|
# lvds block needs _PN_
|
||||||
pad_split = io_pad.split('_')
|
pad_split = io_pad.split('_')
|
||||||
assert pad_split[1] == 'P'
|
assert pad_split[1] == 'P'
|
||||||
io_pad = f"{pad_split[0]}_PN_{pad_split[2]}"
|
io_pad = f"{pad_split[0]}_PN_{pad_split[2]}"
|
||||||
else:
|
else:
|
||||||
assert "RXP" in io_pad
|
assert "RXP" in io_pad
|
||||||
# diff input pins are RXPYY and RXNYY
|
# diff input pins are RXPYY and RXNYY
|
||||||
# lvds block needs RXYY
|
# lvds block needs RXYY
|
||||||
io_pad = io_pad.replace("RXP", "RX")
|
io_pad = io_pad.replace("RXP", "RX")
|
||||||
|
|
||||||
platform.add_extension([
|
platform.add_extension([
|
||||||
|
|
|
@ -286,11 +286,11 @@ class EfinityToolchain(GenericToolchain):
|
||||||
|
|
||||||
# Some IO blocks don't have Python API so we need to configure them
|
# Some IO blocks don't have Python API so we need to configure them
|
||||||
# directly in the peri.xml file
|
# directly in the peri.xml file
|
||||||
# We also need to configure the bank voltage here
|
# We also need to configure the bank voltage here
|
||||||
if self.ifacewriter.xml_blocks or self.platform.iobank_info:
|
if self.ifacewriter.xml_blocks or self.platform.iobank_info:
|
||||||
self.ifacewriter.generate_xml_blocks()
|
self.ifacewriter.generate_xml_blocks()
|
||||||
|
|
||||||
# Because the Python API is sometimes bugged, we need to tweak the generated xml
|
# Because the Python API is sometimes bugged, we need to tweak the generated xml
|
||||||
if self.ifacewriter.fix_xml:
|
if self.ifacewriter.fix_xml:
|
||||||
self.ifacewriter.fix_xml_values()
|
self.ifacewriter.fix_xml_values()
|
||||||
|
|
||||||
|
|
|
@ -329,7 +329,7 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True)
|
||||||
cmd += 'design.set_property("{}", "FEEDBACK_MODE", "{}", "PLL")\n'.format(name, "LOCAL" if feedback_clk < 1 else "CORE")
|
cmd += 'design.set_property("{}", "FEEDBACK_MODE", "{}", "PLL")\n'.format(name, "LOCAL" if feedback_clk < 1 else "CORE")
|
||||||
cmd += 'design.set_property("{}", "FEEDBACK_CLK", "CLK{}", "PLL")\n'.format(name, 0 if feedback_clk < 1 else feedback_clk)
|
cmd += 'design.set_property("{}", "FEEDBACK_CLK", "CLK{}", "PLL")\n'.format(name, 0 if feedback_clk < 1 else feedback_clk)
|
||||||
|
|
||||||
# auto_calc_pll_clock is always working with Titanium and only working when feedback is unused for Trion
|
# auto_calc_pll_clock is always working with Titanium and only working when feedback is unused for Trion
|
||||||
if block["feedback"] == -1 or block["version"] == "V3":
|
if block["feedback"] == -1 or block["version"] == "V3":
|
||||||
cmd += "target_freq = {\n"
|
cmd += "target_freq = {\n"
|
||||||
for i, clock in enumerate(block["clk_out"]):
|
for i, clock in enumerate(block["clk_out"]):
|
||||||
|
|
|
@ -78,7 +78,7 @@ class GowinToolchain(GenericToolchain):
|
||||||
|
|
||||||
for name, pin, other in flat_sc:
|
for name, pin, other in flat_sc:
|
||||||
if pin != "X":
|
if pin != "X":
|
||||||
t_name = name.split('[') # avoid index pins
|
t_name = name.split('[') # avoid index pins
|
||||||
tmp_name = t_name[0]
|
tmp_name = t_name[0]
|
||||||
if tmp_name[-2:] == "_p":
|
if tmp_name[-2:] == "_p":
|
||||||
pn = tmp_name[:-2] + "_n"
|
pn = tmp_name[:-2] + "_n"
|
||||||
|
|
|
@ -158,10 +158,10 @@ class EFINIXPLL(LiteXModule):
|
||||||
# Pre-Divider (N).
|
# Pre-Divider (N).
|
||||||
# -----------------
|
# -----------------
|
||||||
# F_PFD is between 10e6 and 100e6
|
# F_PFD is between 10e6 and 100e6
|
||||||
# so limit search to only acceptable factors
|
# so limit search to only acceptable factors
|
||||||
N_min = int(math.ceil(clk_in_freq / pfd_range[1]))
|
N_min = int(math.ceil(clk_in_freq / pfd_range[1]))
|
||||||
N_max = int(math.floor(clk_in_freq / pfd_range[0]))
|
N_max = int(math.floor(clk_in_freq / pfd_range[0]))
|
||||||
## limit
|
## limit
|
||||||
### when fin is below FPLL_MAX min is < 1
|
### when fin is below FPLL_MAX min is < 1
|
||||||
if N_min < 1:
|
if N_min < 1:
|
||||||
N_min = 1
|
N_min = 1
|
||||||
|
@ -200,7 +200,7 @@ class EFINIXPLL(LiteXModule):
|
||||||
params_list = []
|
params_list = []
|
||||||
for n in range(N_min, N_max + 1):
|
for n in range(N_min, N_max + 1):
|
||||||
fpfd_tmp = clk_in_freq / n
|
fpfd_tmp = clk_in_freq / n
|
||||||
# limit range using FVCO_MAX & FVCO_MIN
|
# limit range using FVCO_MAX & FVCO_MIN
|
||||||
# fVCO = fPFD * M * O * Cfbk
|
# fVCO = fPFD * M * O * Cfbk
|
||||||
# so:
|
# so:
|
||||||
# fVCO
|
# fVCO
|
||||||
|
|
|
@ -32,7 +32,7 @@ class NXOSCA(LiteXModule):
|
||||||
clk_hf_freq_range = (1.76, 450e6)
|
clk_hf_freq_range = (1.76, 450e6)
|
||||||
clk_hf_freq = 450e6
|
clk_hf_freq = 450e6
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, platform=None):
|
||||||
self.logger = logging.getLogger("NXOSCA")
|
self.logger = logging.getLogger("NXOSCA")
|
||||||
self.logger.info("Creating NXOSCA.")
|
self.logger.info("Creating NXOSCA.")
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ class NXOSCA(LiteXModule):
|
||||||
self.hfsdc_clk_out = {}
|
self.hfsdc_clk_out = {}
|
||||||
self.lf_clk_out = None
|
self.lf_clk_out = None
|
||||||
self.params = {}
|
self.params = {}
|
||||||
|
self.platform = platform
|
||||||
|
|
||||||
def create_hf_clk(self, cd, freq, margin=.05):
|
def create_hf_clk(self, cd, freq, margin=.05):
|
||||||
"""450 - 1.7 Mhz Clk"""
|
"""450 - 1.7 Mhz Clk"""
|
||||||
|
@ -86,21 +87,29 @@ class NXOSCA(LiteXModule):
|
||||||
|
|
||||||
def do_finalize(self):
|
def do_finalize(self):
|
||||||
if self.hf_clk_out:
|
if self.hf_clk_out:
|
||||||
divisor = self.compute_divisor(self.hf_clk_out[1], self.hf_clk_out[2])
|
clk_freq = self.hf_clk_out[1]
|
||||||
|
divisor = self.compute_divisor(clk_freq, self.hf_clk_out[2])
|
||||||
self.params["i_HFOUTEN"] = 0b1
|
self.params["i_HFOUTEN"] = 0b1
|
||||||
self.params["p_HF_CLK_DIV"] = divisor
|
self.params["p_HF_CLK_DIV"] = divisor
|
||||||
self.params["o_HFCLKOUT"] = self.hf_clk_out[0]
|
self.params["o_HFCLKOUT"] = self.hf_clk_out[0]
|
||||||
self.params["p_HF_OSC_EN"] = "ENABLED"
|
self.params["p_HF_OSC_EN"] = "ENABLED"
|
||||||
|
if self.platform:
|
||||||
|
self.platform.add_platform_command("create_clock -period {} -name OSCA_HFCLKOUT [get_pins OSCA.OSCA_inst/HFCLKOUT]".format(str(1e9/clk_freq)))
|
||||||
|
|
||||||
if self.hfsdc_clk_out:
|
if self.hfsdc_clk_out:
|
||||||
divisor = self.compute_divisor(self.hfsdc_clk_out[1], self.hfsdc_clk_out[2])
|
clk_freq = self.hf_clk_out[1]
|
||||||
|
divisor = self.compute_divisor(clk_freq, self.hfsdc_clk_out[2])
|
||||||
self.params["i_HFSDSCEN"] = 0b1
|
self.params["i_HFSDSCEN"] = 0b1
|
||||||
self.params["p_HF_SED_SEC_DIV"] = divisor
|
self.params["p_HF_SED_SEC_DIV"] = divisor
|
||||||
self.params["o_HFSDCOUT"] = self.hfsdc_clk_out[0]
|
self.params["o_HFSDCOUT"] = self.hfsdc_clk_out[0]
|
||||||
|
if self.platform:
|
||||||
|
self.platform.add_platform_command("create_clock -period {} -name OSCA_HFSDCOUT [get_pins OSCA.OSCA_inst/HFSDCOUT]".format(str(1e9/clk_freq)))
|
||||||
|
|
||||||
if self.lf_clk_out is not None:
|
if self.lf_clk_out is not None:
|
||||||
self.params["o_LFCLKOUT"] = self.lf_clk_out[0]
|
self.params["o_LFCLKOUT"] = self.lf_clk_out[0]
|
||||||
self.params["p_LF_OUTPUT_EN"] = "ENABLED"
|
self.params["p_LF_OUTPUT_EN"] = "ENABLED"
|
||||||
|
if self.platform:
|
||||||
|
self.platform.add_platform_command("create_clock -period {} -name OSCA_LF_OUTPUT_EN [get_pins OSCA.OSCA_inst/LF_OUTPUT_EN]".format(str(1e9/128e3)))
|
||||||
|
|
||||||
self.specials += Instance("OSCA", **self.params)
|
self.specials += Instance("OSCA", **self.params)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
#
|
||||||
|
# This file is part of MiSoC and has been adapted/modified for Litex.
|
||||||
|
#
|
||||||
|
# Copyright 2007-2023 / M-Labs Ltd
|
||||||
|
# Copyright 2012-2015 / Enjoy-Digital
|
||||||
|
# Copyright from Misoc LICENCE file added above
|
||||||
|
#
|
||||||
|
# Copyright 2023 Andrew Dennison <andrew@motec.com.au>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
from migen import *
|
||||||
|
from litex.gen import *
|
||||||
|
from litex.soc.interconnect import wishbone
|
||||||
|
from litex.soc.interconnect.csr_eventmanager import *
|
||||||
|
|
||||||
|
# I2C-----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"I2CMaster",
|
||||||
|
"I2C_XFER_ADDR", "I2C_CONFIG_ADDR",
|
||||||
|
"I2C_ACK", "I2C_READ", "I2C_WRITE", "I2C_STOP", "I2C_START", "I2C_IDLE",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class I2CClockGen(LiteXModule):
|
||||||
|
def __init__(self, width):
|
||||||
|
self.load = Signal(width)
|
||||||
|
self.clk2x = Signal()
|
||||||
|
|
||||||
|
cnt = Signal.like(self.load)
|
||||||
|
self.comb += [
|
||||||
|
self.clk2x.eq(cnt == 0),
|
||||||
|
]
|
||||||
|
self.sync += [
|
||||||
|
If(self.clk2x,
|
||||||
|
cnt.eq(self.load),
|
||||||
|
).Else(
|
||||||
|
cnt.eq(cnt - 1),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class I2CMasterMachine(LiteXModule):
|
||||||
|
def __init__(self, clock_width):
|
||||||
|
self.scl_o = Signal(reset=1)
|
||||||
|
self.sda_o = Signal(reset=1)
|
||||||
|
self.sda_i = Signal()
|
||||||
|
|
||||||
|
self.cg = CEInserter()(I2CClockGen(clock_width))
|
||||||
|
self.idle = Signal()
|
||||||
|
self.start = Signal()
|
||||||
|
self.stop = Signal()
|
||||||
|
self.write = Signal()
|
||||||
|
self.read = Signal()
|
||||||
|
self.ack = Signal()
|
||||||
|
self.data = Signal(8)
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
busy = Signal()
|
||||||
|
bits = Signal(4)
|
||||||
|
|
||||||
|
fsm = CEInserter()(FSM("IDLE"))
|
||||||
|
self.fsm = fsm
|
||||||
|
|
||||||
|
fsm.act("IDLE",
|
||||||
|
# Valid combinations (lowest to highest priority):
|
||||||
|
# stop: lowest priority
|
||||||
|
# read (& optional stop with automatic NACK)
|
||||||
|
# write (& optional stop)
|
||||||
|
# start (indicates start or restart)
|
||||||
|
# start & write (& optional stop)
|
||||||
|
# start & write & read (& optional stop)
|
||||||
|
# lowest priority
|
||||||
|
# *** TODO: support compound commands with I2CMaster ***
|
||||||
|
If(self.stop & ~self.scl_o,
|
||||||
|
# stop is only valid after an ACK
|
||||||
|
NextState("STOP0"),
|
||||||
|
),
|
||||||
|
If(self.read,
|
||||||
|
# post decrement so read first bit and shift in 7
|
||||||
|
NextValue(bits, 8-1),
|
||||||
|
NextState("READ0"),
|
||||||
|
),
|
||||||
|
If(self.write,
|
||||||
|
NextValue(bits, 8),
|
||||||
|
NextState("WRITE0"),
|
||||||
|
),
|
||||||
|
# start could be requesting a restart
|
||||||
|
If(self.start,
|
||||||
|
NextState("RESTART0"),
|
||||||
|
),
|
||||||
|
# highest priority: start only if scl is high
|
||||||
|
If(self.start & self.scl_o,
|
||||||
|
NextState("START0"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("START0",
|
||||||
|
# Always entered with scl_o = 1
|
||||||
|
NextValue(self.sda_o, 0),
|
||||||
|
NextState("IDLE"))
|
||||||
|
|
||||||
|
fsm.act("RESTART0",
|
||||||
|
# Only entered from IDLE with scl_o = 0
|
||||||
|
NextValue(self.sda_o, 1),
|
||||||
|
NextState("RESTART1"))
|
||||||
|
fsm.act("RESTART1",
|
||||||
|
NextValue(self.scl_o, 1),
|
||||||
|
NextState("START0"))
|
||||||
|
|
||||||
|
fsm.act("STOP0",
|
||||||
|
# Only entered from IDLE with scl_o = 0
|
||||||
|
NextValue(self.sda_o, 0),
|
||||||
|
NextState("STOP1"))
|
||||||
|
fsm.act("STOP1",
|
||||||
|
NextValue(self.scl_o, 1),
|
||||||
|
NextState("STOP2"))
|
||||||
|
fsm.act("STOP2",
|
||||||
|
NextValue(self.sda_o, 1),
|
||||||
|
NextState("IDLE"))
|
||||||
|
|
||||||
|
fsm.act("WRITE0",
|
||||||
|
NextValue(self.scl_o, 0),
|
||||||
|
If(bits == 0,
|
||||||
|
NextValue(self.sda_o, 1),
|
||||||
|
NextState("READACK0"),
|
||||||
|
).Else(
|
||||||
|
NextValue(self.sda_o, self.data[7]),
|
||||||
|
NextState("WRITE1"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("WRITE1",
|
||||||
|
NextValue(self.scl_o, 1),
|
||||||
|
NextValue(self.data[1:], self.data[:-1]),
|
||||||
|
NextValue(bits, bits - 1),
|
||||||
|
NextState("WRITE0"),
|
||||||
|
)
|
||||||
|
fsm.act("READACK0",
|
||||||
|
NextValue(self.scl_o, 1),
|
||||||
|
NextState("READACK1"),
|
||||||
|
)
|
||||||
|
fsm.act("READACK1",
|
||||||
|
# ACK => IDLE always with scl_o = 0
|
||||||
|
NextValue(self.scl_o, 0),
|
||||||
|
NextValue(self.ack, ~self.sda_i),
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
|
||||||
|
fsm.act("READ0",
|
||||||
|
# ACK => IDLE => READ0 always with scl_o = 0
|
||||||
|
NextValue(self.scl_o, 1),
|
||||||
|
NextState("READ1"),
|
||||||
|
)
|
||||||
|
fsm.act("READ1",
|
||||||
|
NextValue(self.data[0], self.sda_i),
|
||||||
|
NextValue(self.scl_o, 0),
|
||||||
|
If(bits == 0,
|
||||||
|
NextValue(self.sda_o, ~self.ack),
|
||||||
|
NextState("WRITEACK0"),
|
||||||
|
).Else(
|
||||||
|
#NextValue(self.sda_o, 1), must already be high
|
||||||
|
NextState("READ2"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fsm.act("READ2",
|
||||||
|
NextValue(self.scl_o, 1),
|
||||||
|
NextValue(self.data[1:], self.data[:-1]),
|
||||||
|
NextValue(bits, bits - 1),
|
||||||
|
NextState("READ1"),
|
||||||
|
)
|
||||||
|
fsm.act("WRITEACK0",
|
||||||
|
NextValue(self.scl_o, 1),
|
||||||
|
NextState("WRITEACK1"),
|
||||||
|
)
|
||||||
|
fsm.act("WRITEACK1",
|
||||||
|
# ACK => IDLE always with scl_o = 0
|
||||||
|
NextValue(self.scl_o, 0),
|
||||||
|
NextValue(self.sda_o, 1),
|
||||||
|
NextState("IDLE")
|
||||||
|
)
|
||||||
|
|
||||||
|
run = Signal()
|
||||||
|
self.comb += [
|
||||||
|
run.eq(self.start | self.stop | self.write | self.read),
|
||||||
|
self.idle.eq(~run & fsm.ongoing("IDLE")),
|
||||||
|
self.cg.ce.eq(~self.idle),
|
||||||
|
fsm.ce.eq(run | self.cg.clk2x),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Registers:
|
||||||
|
# config = Record([
|
||||||
|
# ("div", 20),
|
||||||
|
# ])
|
||||||
|
# xfer = Record([
|
||||||
|
# ("data", 8),
|
||||||
|
# ("ack", 1),
|
||||||
|
# ("read", 1),
|
||||||
|
# ("write", 1),
|
||||||
|
# ("start", 1),
|
||||||
|
# ("stop", 1),
|
||||||
|
# ("idle", 1),
|
||||||
|
# ])
|
||||||
|
class I2CMaster(LiteXModule):
|
||||||
|
def __init__(self, pads, bus=None):
|
||||||
|
if bus is None:
|
||||||
|
bus = wishbone.Interface(data_width=32)
|
||||||
|
self.bus = bus
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# Wishbone
|
||||||
|
self.i2c = i2c = I2CMasterMachine(
|
||||||
|
clock_width=20)
|
||||||
|
|
||||||
|
self.sync += [
|
||||||
|
# read
|
||||||
|
If(bus.adr[0],
|
||||||
|
bus.dat_r.eq(i2c.cg.load),
|
||||||
|
).Else(
|
||||||
|
bus.dat_r.eq(Cat(i2c.data, i2c.ack, C(0, 4), i2c.idle)),
|
||||||
|
),
|
||||||
|
|
||||||
|
# write
|
||||||
|
i2c.read.eq(0),
|
||||||
|
i2c.write.eq(0),
|
||||||
|
i2c.start.eq(0),
|
||||||
|
i2c.stop.eq(0),
|
||||||
|
|
||||||
|
bus.ack.eq(0),
|
||||||
|
If(bus.cyc & bus.stb & ~bus.ack,
|
||||||
|
bus.ack.eq(1),
|
||||||
|
If(bus.we,
|
||||||
|
If(bus.adr[0],
|
||||||
|
i2c.cg.load.eq(bus.dat_w),
|
||||||
|
).Else(
|
||||||
|
i2c.data.eq(bus.dat_w[0:8]),
|
||||||
|
i2c.ack.eq(bus.dat_w[8]),
|
||||||
|
i2c.read.eq(bus.dat_w[9]),
|
||||||
|
i2c.write.eq(bus.dat_w[10]),
|
||||||
|
i2c.start.eq(bus.dat_w[11]),
|
||||||
|
i2c.stop.eq(bus.dat_w[12]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# I/O
|
||||||
|
self.scl_t = TSTriple()
|
||||||
|
self.scl_tristate = self.scl_t.get_tristate(pads.scl)
|
||||||
|
self.comb += [
|
||||||
|
self.scl_t.oe.eq(~i2c.scl_o),
|
||||||
|
self.scl_t.o.eq(0),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.sda_t = TSTriple()
|
||||||
|
self.sda_tristate = self.sda_t.get_tristate(pads.sda)
|
||||||
|
|
||||||
|
self.scl_i_n = Signal() # previous scl_t.i
|
||||||
|
self.sda_oe_n = Signal() # previous sda_t.oe
|
||||||
|
self.sync += [
|
||||||
|
self.scl_i_n.eq(self.scl_t.i),
|
||||||
|
self.sda_oe_n.eq(self.sda_t.oe),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.comb += [
|
||||||
|
self.sda_t.oe.eq(self.sda_oe_n),
|
||||||
|
# only change SDA when SCL is stable
|
||||||
|
If(self.scl_i_n == i2c.scl_o,
|
||||||
|
self.sda_t.oe.eq(~i2c.sda_o),
|
||||||
|
),
|
||||||
|
self.sda_t.o.eq(0),
|
||||||
|
i2c.sda_i.eq(self.sda_t.i),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Event Manager.
|
||||||
|
self.ev = EventManager()
|
||||||
|
self.ev.idle = EventSourceProcess(edge="rising")
|
||||||
|
self.ev.finalize()
|
||||||
|
self.comb += self.ev.idle.trigger.eq(i2c.idle)
|
||||||
|
|
||||||
|
I2C_XFER_ADDR, I2C_CONFIG_ADDR = range(2)
|
||||||
|
(
|
||||||
|
I2C_ACK,
|
||||||
|
I2C_READ,
|
||||||
|
I2C_WRITE,
|
||||||
|
I2C_START,
|
||||||
|
I2C_STOP,
|
||||||
|
I2C_IDLE,
|
||||||
|
) = (1 << i for i in range(8, 14))
|
|
@ -79,10 +79,10 @@ class NXLRAM(LiteXModule):
|
||||||
wren = Signal()
|
wren = Signal()
|
||||||
self.comb += [
|
self.comb += [
|
||||||
datain.eq(self.bus.dat_w[32*w:32*(w+1)]),
|
datain.eq(self.bus.dat_w[32*w:32*(w+1)]),
|
||||||
self.bus.dat_r[32*w:32*(w+1)].eq(dataout),
|
|
||||||
If(self.bus.adr[14:14+self.depth_cascading.bit_length()] == d,
|
If(self.bus.adr[14:14+self.depth_cascading.bit_length()] == d,
|
||||||
cs.eq(1),
|
cs.eq(1),
|
||||||
wren.eq(self.bus.we & self.bus.stb & self.bus.cyc)
|
wren.eq(self.bus.we & self.bus.stb & self.bus.cyc),
|
||||||
|
self.bus.dat_r[32*w:32*(w+1)].eq(dataout),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
lram_block = Instance("SP512K",
|
lram_block = Instance("SP512K",
|
||||||
|
|
|
@ -653,7 +653,10 @@ class VideoFrameBuffer(LiteXModule):
|
||||||
|
|
||||||
self.depth = depth = {
|
self.depth = depth = {
|
||||||
"rgb888" : 32,
|
"rgb888" : 32,
|
||||||
"rgb565" : 16
|
"rgb565" : 16,
|
||||||
|
"rgb332" : 8,
|
||||||
|
"mono8" : 8,
|
||||||
|
"mono1" : 1,
|
||||||
}[format]
|
}[format]
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
@ -728,16 +731,34 @@ class VideoFrameBuffer(LiteXModule):
|
||||||
|
|
||||||
if (depth == 32):
|
if (depth == 32):
|
||||||
self.comb += [
|
self.comb += [
|
||||||
source.r.eq(video_pipe_source.data[ 0: 8]),
|
source.r.eq(video_pipe_source.data[ 0: 8]),
|
||||||
source.g.eq(video_pipe_source.data[ 8:16]),
|
source.g.eq(video_pipe_source.data[ 8:16]),
|
||||||
source.b.eq(video_pipe_source.data[16:24]),
|
source.b.eq(video_pipe_source.data[16:24]),
|
||||||
]
|
]
|
||||||
else: # depth == 16
|
elif (depth == 16):
|
||||||
self.comb += [
|
self.comb += [
|
||||||
source.r.eq(Cat(Signal(3, reset = 0), video_pipe_source.data[11:16])),
|
source.r.eq(Cat(Signal(3, reset = 0), video_pipe_source.data[11:16])),
|
||||||
source.g.eq(Cat(Signal(2, reset = 0), video_pipe_source.data[ 5:11])),
|
source.g.eq(Cat(Signal(2, reset = 0), video_pipe_source.data[ 5:11])),
|
||||||
source.b.eq(Cat(Signal(3, reset = 0), video_pipe_source.data[ 0: 5])),
|
source.b.eq(Cat(Signal(3, reset = 0), video_pipe_source.data[ 0: 5])),
|
||||||
]
|
]
|
||||||
|
elif (depth == 8 and format == "rgb332"):
|
||||||
|
self.comb += [
|
||||||
|
source.r.eq(Cat(Signal(5, reset = 0), video_pipe_source.data[5:8])),
|
||||||
|
source.g.eq(Cat(Signal(5, reset = 0), video_pipe_source.data[2:5])),
|
||||||
|
source.b.eq(Cat(Signal(6, reset = 0), video_pipe_source.data[0:2])),
|
||||||
|
]
|
||||||
|
elif (depth == 8 and format == "mono8"):
|
||||||
|
self.comb += [
|
||||||
|
source.r.eq(video_pipe_source.data[0:8]),
|
||||||
|
source.g.eq(video_pipe_source.data[0:8]),
|
||||||
|
source.b.eq(video_pipe_source.data[0:8]),
|
||||||
|
]
|
||||||
|
else: # depth == 1
|
||||||
|
self.comb += [
|
||||||
|
source.r.eq(Cat(Signal(7, reset = 0), video_pipe_source.data[0:1])),
|
||||||
|
source.g.eq(Cat(Signal(7, reset = 0), video_pipe_source.data[0:1])),
|
||||||
|
source.b.eq(Cat(Signal(7, reset = 0), video_pipe_source.data[0:1])),
|
||||||
|
]
|
||||||
|
|
||||||
# Underflow.
|
# Underflow.
|
||||||
self.comb += self.underflow.eq(~source.valid)
|
self.comb += self.underflow.eq(~source.valid)
|
||||||
|
|
|
@ -1512,7 +1512,7 @@ class LiteXSoC(SoC):
|
||||||
self.add_config(name, identifier)
|
self.add_config(name, identifier)
|
||||||
|
|
||||||
# Add UART -------------------------------------------------------------------------------------
|
# Add UART -------------------------------------------------------------------------------------
|
||||||
def add_uart(self, name="uart", uart_name="serial", baudrate=115200, fifo_depth=16):
|
def add_uart(self, name="uart", uart_name="serial", uart_pads=None, baudrate=115200, fifo_depth=16):
|
||||||
# Imports.
|
# Imports.
|
||||||
from litex.soc.cores.uart import UART, UARTCrossover
|
from litex.soc.cores.uart import UART, UARTCrossover
|
||||||
|
|
||||||
|
@ -1529,8 +1529,9 @@ class LiteXSoC(SoC):
|
||||||
"usb_acm",
|
"usb_acm",
|
||||||
"serial(x)",
|
"serial(x)",
|
||||||
]
|
]
|
||||||
uart_pads_name = "serial" if uart_name == "sim" else uart_name
|
if uart_pads is None:
|
||||||
uart_pads = self.platform.request(uart_pads_name, loose=True)
|
uart_pads_name = "serial" if uart_name == "sim" else uart_name
|
||||||
|
uart_pads = self.platform.request(uart_pads_name, loose=True)
|
||||||
uart_phy = None
|
uart_phy = None
|
||||||
uart = None
|
uart = None
|
||||||
uart_kwargs = {
|
uart_kwargs = {
|
||||||
|
|
|
@ -386,7 +386,7 @@ class PacketFIFO(LiteXModule):
|
||||||
sink.connect(param_fifo.sink, keep=set([e[0] for e in param_layout])),
|
sink.connect(param_fifo.sink, keep=set([e[0] for e in param_layout])),
|
||||||
sink.connect(payload_fifo.sink, keep=set([e[0] for e in payload_layout] + ["last"])),
|
sink.connect(payload_fifo.sink, keep=set([e[0] for e in payload_layout] + ["last"])),
|
||||||
param_fifo.sink.valid.eq(sink.valid & sink.last),
|
param_fifo.sink.valid.eq(sink.valid & sink.last),
|
||||||
payload_fifo.sink.valid.eq(sink.valid & payload_fifo.sink.ready),
|
payload_fifo.sink.valid.eq(sink.valid & param_fifo.sink.ready),
|
||||||
sink.ready.eq(param_fifo.sink.ready & payload_fifo.sink.ready),
|
sink.ready.eq(param_fifo.sink.ready & payload_fifo.sink.ready),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -15,35 +15,35 @@ import argparse
|
||||||
from migen import *
|
from migen import *
|
||||||
|
|
||||||
from litex.build.generic_platform import *
|
from litex.build.generic_platform import *
|
||||||
from litex.build.sim import SimPlatform
|
from litex.build.sim import SimPlatform
|
||||||
from litex.build.sim.config import SimConfig
|
from litex.build.sim.config import SimConfig
|
||||||
|
|
||||||
from litex.soc.integration.common import *
|
from litex.soc.integration.common import *
|
||||||
from litex.soc.integration.soc_core import *
|
from litex.soc.integration.soc_core import *
|
||||||
from litex.soc.integration.builder import *
|
from litex.soc.integration.builder import *
|
||||||
from litex.soc.integration.soc import *
|
from litex.soc.integration.soc import *
|
||||||
from litex.soc.cores.bitbang import *
|
|
||||||
from litex.soc.cores.gpio import GPIOTristate
|
|
||||||
from litex.soc.cores.cpu import CPUS
|
|
||||||
|
|
||||||
from litedram import modules as litedram_modules
|
from litex.soc.cores.bitbang import *
|
||||||
from litedram.modules import parse_spd_hexdump
|
from litex.soc.cores.gpio import GPIOTristate
|
||||||
|
from litex.soc.cores.cpu import CPUS
|
||||||
|
from litex.soc.cores.video import VideoGenericPHY
|
||||||
|
|
||||||
|
from litedram import modules as litedram_modules
|
||||||
|
from litedram.modules import parse_spd_hexdump
|
||||||
from litedram.phy.model import sdram_module_nphases, get_sdram_phy_settings
|
from litedram.phy.model import sdram_module_nphases, get_sdram_phy_settings
|
||||||
from litedram.phy.model import SDRAMPHYModel
|
from litedram.phy.model import SDRAMPHYModel
|
||||||
|
|
||||||
from liteeth.phy.gmii import LiteEthPHYGMII
|
from liteeth.common import *
|
||||||
from liteeth.phy.xgmii import LiteEthPHYXGMII
|
from liteeth.phy.gmii import LiteEthPHYGMII
|
||||||
from liteeth.phy.model import LiteEthPHYModel
|
from liteeth.phy.xgmii import LiteEthPHYXGMII
|
||||||
from liteeth.mac import LiteEthMAC
|
from liteeth.phy.model import LiteEthPHYModel
|
||||||
from liteeth.core.arp import LiteEthARP
|
from liteeth.mac import LiteEthMAC
|
||||||
from liteeth.core.ip import LiteEthIP
|
from liteeth.core.arp import LiteEthARP
|
||||||
from liteeth.core.udp import LiteEthUDP
|
from liteeth.core.ip import LiteEthIP
|
||||||
from liteeth.core.icmp import LiteEthICMP
|
from liteeth.core.udp import LiteEthUDP
|
||||||
from liteeth.core import LiteEthUDPIPCore
|
from liteeth.core.icmp import LiteEthICMP
|
||||||
|
from liteeth.core import LiteEthUDPIPCore
|
||||||
from liteeth.frontend.etherbone import LiteEthEtherbone
|
from liteeth.frontend.etherbone import LiteEthEtherbone
|
||||||
from liteeth.common import *
|
|
||||||
|
|
||||||
from litex.soc.cores.video import VideoGenericPHY
|
|
||||||
|
|
||||||
from litescope import LiteScopeAnalyzer
|
from litescope import LiteScopeAnalyzer
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# This file is part of MiSoC and has been adapted/modified for Litex.
|
||||||
|
#
|
||||||
|
# Copyright 2007-2023 / M-Labs Ltd
|
||||||
|
# Copyright 2012-2015 / Enjoy-Digital
|
||||||
|
# Copyright from Misoc LICENCE file added above
|
||||||
|
#
|
||||||
|
# Copyright 2023 Andrew Dennison <andrew@motec.com.au>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from migen import *
|
||||||
|
from migen.fhdl.specials import Tristate
|
||||||
|
|
||||||
|
from litex.soc.cores.i2c import *
|
||||||
|
|
||||||
|
|
||||||
|
class _MockPads:
|
||||||
|
def __init__(self):
|
||||||
|
self.scl = Signal()
|
||||||
|
self.sda = Signal()
|
||||||
|
|
||||||
|
|
||||||
|
class _MockTristateImpl(Module):
|
||||||
|
def __init__(self, t):
|
||||||
|
t.i_mock = Signal(reset=True)
|
||||||
|
self.comb += [
|
||||||
|
If(t.oe,
|
||||||
|
t.target.eq(t.o),
|
||||||
|
t.i.eq(t.o),
|
||||||
|
).Else(
|
||||||
|
t.target.eq(t.i_mock),
|
||||||
|
t.i.eq(t.i_mock),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class _MockTristate:
|
||||||
|
"""A mock `Tristate` for simulation
|
||||||
|
|
||||||
|
This simulation ensures the TriState input (_i) tracks the output (_o) when output enable
|
||||||
|
(_oe) = 1. A new i_mock `Signal` is added - this can be written to in the simulation to represent
|
||||||
|
input from the external device.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
class TestMyModule(unittest.TestCase):
|
||||||
|
def test_mymodule(self):
|
||||||
|
dut = MyModule()
|
||||||
|
io = Signal()
|
||||||
|
dut.io_t = TSTriple()
|
||||||
|
self.io_tristate = self.io_t.get_tristate(io)
|
||||||
|
|
||||||
|
dut.comb += [
|
||||||
|
dut.io_t.oe.eq(signal_for_oe),
|
||||||
|
dut.io_t.o.eq(signal_for_o),
|
||||||
|
signal_for_i.eq(dut.io_t.i),
|
||||||
|
]
|
||||||
|
|
||||||
|
def generator()
|
||||||
|
yield dut.io_tristate.i_mock.eq(some_value)
|
||||||
|
if (yield dut.io_t.oe):
|
||||||
|
self.assertEqual((yield dut.scl_t.i), (yield dut.io_t.o))
|
||||||
|
else:
|
||||||
|
self.assertEqual((yield dut.scl_t.i), some_value)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def lower(t):
|
||||||
|
return _MockTristateImpl(t)
|
||||||
|
|
||||||
|
|
||||||
|
class TestI2C(unittest.TestCase):
|
||||||
|
def test_i2c(self):
|
||||||
|
pads = _MockPads()
|
||||||
|
dut = I2CMaster(pads)
|
||||||
|
|
||||||
|
def check_trans(scl, sda, msg=""):
|
||||||
|
scl, sda = int(scl), int(sda)
|
||||||
|
scl_init, sda_init = (yield dut.scl_t.i), (yield dut.sda_t.i)
|
||||||
|
timeout = 0
|
||||||
|
while True:
|
||||||
|
scl_now, sda_now = (yield dut.scl_t.i), (yield dut.sda_t.i)
|
||||||
|
if scl_now == scl and sda_now == sda:
|
||||||
|
return
|
||||||
|
timeout += 1
|
||||||
|
self.assertLess(timeout, 20,
|
||||||
|
f"\n*** {msg} timeout. Waiting for: " +
|
||||||
|
f"scl:{scl_now} checking:{scl_init}=>{scl} " +
|
||||||
|
f"sda:{sda_now} checking:{sda_init}=>{sda} ***"
|
||||||
|
)
|
||||||
|
yield
|
||||||
|
|
||||||
|
def wait_idle(do=lambda: ()):
|
||||||
|
timeout = 0
|
||||||
|
while True:
|
||||||
|
timeout += 1
|
||||||
|
self.assertLess(timeout, 20)
|
||||||
|
idle = ((yield from dut.bus.read(I2C_XFER_ADDR)) & I2C_IDLE) != 0
|
||||||
|
if idle:
|
||||||
|
return
|
||||||
|
yield
|
||||||
|
|
||||||
|
def write_bit(value):
|
||||||
|
# print(f"write_bit:{value}")
|
||||||
|
yield from check_trans(scl=False, sda=value)
|
||||||
|
yield from check_trans(scl=True, sda=value)
|
||||||
|
|
||||||
|
def write_ack(value):
|
||||||
|
# print(f"write_ack:{value}")
|
||||||
|
yield from check_trans(scl=False, sda=not value)
|
||||||
|
yield from check_trans(scl=True, sda=not value)
|
||||||
|
yield from wait_idle()
|
||||||
|
|
||||||
|
def read_bit(value):
|
||||||
|
print(f"read_bit:{value}")
|
||||||
|
yield dut.sda_tristate.i_mock.eq(value)
|
||||||
|
yield from check_trans(scl=True, sda=value)
|
||||||
|
yield from check_trans(scl=False, sda=value)
|
||||||
|
yield dut.sda_tristate.i_mock.eq(True)
|
||||||
|
|
||||||
|
def read_ack(value):
|
||||||
|
#print(f"read_ack:{value}")
|
||||||
|
yield from check_trans(scl=False, sda=True)
|
||||||
|
yield dut.sda_tristate.i_mock.eq(not value)
|
||||||
|
yield from check_trans(scl=True, sda=not value)
|
||||||
|
yield from wait_idle()
|
||||||
|
yield dut.sda_tristate.i_mock.eq(True)
|
||||||
|
ack = ((yield from dut.bus.read(I2C_XFER_ADDR)) & I2C_ACK) != 0
|
||||||
|
self.assertEqual(ack, value)
|
||||||
|
|
||||||
|
def i2c_restart():
|
||||||
|
yield from check_trans(scl=False, sda=True, msg="checking restart precondition")
|
||||||
|
yield from dut.bus.write(I2C_XFER_ADDR, I2C_START)
|
||||||
|
yield from check_trans(scl=False, sda=True, msg="checking restart0")
|
||||||
|
yield from check_trans(scl=True, sda=True, msg="checking restart1")
|
||||||
|
yield from check_trans(scl=True, sda=False, msg="checking start0")
|
||||||
|
yield from wait_idle()
|
||||||
|
|
||||||
|
def i2c_start():
|
||||||
|
yield from check_trans(scl=True, sda=True, msg="checking start precondition")
|
||||||
|
yield from dut.bus.write(I2C_XFER_ADDR, I2C_START)
|
||||||
|
yield from check_trans(scl=True, sda=False, msg="checking start0")
|
||||||
|
yield from wait_idle()
|
||||||
|
|
||||||
|
def i2c_stop():
|
||||||
|
yield from check_trans(scl=False, sda=True, msg="checking stop after read or write")
|
||||||
|
yield from dut.bus.write(I2C_XFER_ADDR, I2C_STOP)
|
||||||
|
yield from check_trans(scl=False, sda=False, msg="checking STOP0")
|
||||||
|
yield from check_trans(scl=True, sda=False, msg="checking STOP1")
|
||||||
|
yield from check_trans(scl=True, sda=True, msg="checking STOP2")
|
||||||
|
yield from wait_idle()
|
||||||
|
|
||||||
|
def i2c_write(value, ack=True):
|
||||||
|
value = int(value)
|
||||||
|
test_bin = "{0:08b}".format(value)
|
||||||
|
# print(f"I2C_WRITE | {hex(value)}:0x{test_bin}")
|
||||||
|
yield from dut.bus.write(I2C_XFER_ADDR, I2C_WRITE | value)
|
||||||
|
for i in list(test_bin):
|
||||||
|
yield from write_bit(int(i))
|
||||||
|
yield from read_ack(True)
|
||||||
|
|
||||||
|
def i2c_read(value, ack=True):
|
||||||
|
value = int(value)
|
||||||
|
test_bin = "{0:08b}".format(value)
|
||||||
|
print(f"I2C_READ | {hex(value)}:0x{test_bin}")
|
||||||
|
yield from dut.bus.write(I2C_XFER_ADDR, I2C_READ | (I2C_ACK if ack else 0))
|
||||||
|
for i in list(test_bin):
|
||||||
|
yield from read_bit(int(i))
|
||||||
|
yield dut.sda_tristate.i_mock.eq(True)
|
||||||
|
data = (yield from dut.bus.read(I2C_XFER_ADDR)) & 0xFF
|
||||||
|
self.assertEqual(data, value)
|
||||||
|
yield from write_ack(ack)
|
||||||
|
|
||||||
|
def check():
|
||||||
|
yield from dut.bus.write(I2C_CONFIG_ADDR, 4)
|
||||||
|
data = (yield from dut.bus.read(I2C_CONFIG_ADDR)) & 0xFF
|
||||||
|
self.assertEqual(data, 4)
|
||||||
|
|
||||||
|
print("write 1 byte 0x18 to address 0x41")
|
||||||
|
yield from i2c_start()
|
||||||
|
yield from i2c_write(0x41 << 1 | 0)
|
||||||
|
yield from i2c_write(0x18, ack=False)
|
||||||
|
yield from i2c_stop()
|
||||||
|
|
||||||
|
print("read 1 byte from address 0x41")
|
||||||
|
yield from i2c_start()
|
||||||
|
yield from i2c_write(0x41 << 1 | 1)
|
||||||
|
yield from i2c_read(0x18, ack=False)
|
||||||
|
|
||||||
|
print("write 2 bytes 0x10 0x00 to address 0x11")
|
||||||
|
yield from i2c_restart()
|
||||||
|
yield from i2c_write(0x11 << 1 | 0)
|
||||||
|
yield from i2c_write(0x10, ack=True)
|
||||||
|
yield from i2c_write(0x00, ack=False)
|
||||||
|
yield from i2c_stop()
|
||||||
|
|
||||||
|
print("read 1 byte from address 0x11")
|
||||||
|
yield from i2c_start()
|
||||||
|
yield from i2c_write(0x11 << 1 | 1)
|
||||||
|
yield from i2c_read(0x81, ack=False)
|
||||||
|
|
||||||
|
print("read 2 bytes from address 0x55")
|
||||||
|
yield from i2c_restart()
|
||||||
|
yield from i2c_write(0x55 << 1 | 1)
|
||||||
|
yield from i2c_read(0xDE, ack=True)
|
||||||
|
yield from i2c_read(0xAD, ack=False)
|
||||||
|
yield from i2c_stop()
|
||||||
|
|
||||||
|
clocks = {
|
||||||
|
"sys": 10,
|
||||||
|
"async": (10, 3),
|
||||||
|
}
|
||||||
|
generators = {
|
||||||
|
"sys": [
|
||||||
|
check(),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
run_simulation(dut, generators, clocks, special_overrides={Tristate: _MockTristate}, vcd_name="i2c.vcd")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue