Merge remote-tracking branch 'origin/master' into vexiiriscv

This commit is contained in:
Dolu1990 2024-08-05 09:07:39 +02:00
commit cb6e28aaa2
16 changed files with 701 additions and 52 deletions

View File

@ -54,9 +54,9 @@ The framework is also far from perfect and we'll be happy to have your [feedback
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!
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).
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.
Thank you for helping us improve LiteX and being part of our community!
# Typical LiteX design flow:
```

View File

@ -99,7 +99,6 @@ class AlteraDDROutputImpl(Module):
o_dataout = o,
)
class AlteraDDROutput:
@staticmethod
def lower(dr):
@ -147,3 +146,103 @@ altera_special_overrides = {
SDROutput: AlteraSDROutput,
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,
}

View File

@ -34,6 +34,8 @@ class AlteraPlatform(GenericPlatform):
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
so = dict(common.altera_special_overrides)
if self.device[:3] == "A5E":
so.update(common.agilex5_special_overrides)
so.update(special_overrides)
return GenericPlatform.get_verilog(self, *args,
special_overrides = so,

View File

@ -162,21 +162,21 @@ class EfinixSDRTristate(Module):
class EfinixDifferentialOutputImpl(Module):
def __init__(self, platform, i, o_p, o_n):
# only keep _p
# only keep _p
io_name = platform.get_pin_name(o_p)
io_pad = platform.get_pad_name(o_p) # need real pad name
io_prop = platform.get_pin_properties(o_p)
if platform.family == "Titanium":
# _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('_')
assert pad_split[1] == 'P'
io_pad = f"{pad_split[0]}_PN_{pad_split[2]}"
else:
assert "TXP" in io_pad
# diff output pins are TXPYY and TXNYY
# lvds block needs TXYY
# lvds block needs TXYY
io_pad = io_pad.replace("TXP", "TX")
platform.add_extension([(io_name, 0, Pins(1))])
@ -206,21 +206,21 @@ class EfinixDifferentialOutput:
class EfinixDifferentialInputImpl(Module):
def __init__(self, platform, i_p, i_n, o):
# only keep _p
# only keep _p
io_name = platform.get_pin_name(i_p)
io_pad = platform.get_pad_name(i_p) # need real pad name
io_prop = platform.get_pin_properties(i_p)
if platform.family == "Titanium":
# _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('_')
assert pad_split[1] == 'P'
io_pad = f"{pad_split[0]}_PN_{pad_split[2]}"
else:
assert "RXP" in io_pad
# diff input pins are RXPYY and RXNYY
# lvds block needs RXYY
# lvds block needs RXYY
io_pad = io_pad.replace("RXP", "RX")
platform.add_extension([

View File

@ -286,11 +286,11 @@ class EfinityToolchain(GenericToolchain):
# Some IO blocks don't have Python API so we need to configure them
# 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:
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:
self.ifacewriter.fix_xml_values()

View File

@ -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_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":
cmd += "target_freq = {\n"
for i, clock in enumerate(block["clk_out"]):

View File

@ -78,7 +78,7 @@ class GowinToolchain(GenericToolchain):
for name, pin, other in flat_sc:
if pin != "X":
t_name = name.split('[') # avoid index pins
t_name = name.split('[') # avoid index pins
tmp_name = t_name[0]
if tmp_name[-2:] == "_p":
pn = tmp_name[:-2] + "_n"

View File

@ -158,10 +158,10 @@ class EFINIXPLL(LiteXModule):
# Pre-Divider (N).
# -----------------
# 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_max = int(math.floor(clk_in_freq / pfd_range[0]))
## limit
## limit
### when fin is below FPLL_MAX min is < 1
if N_min < 1:
N_min = 1
@ -200,7 +200,7 @@ class EFINIXPLL(LiteXModule):
params_list = []
for n in range(N_min, N_max + 1):
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
# so:
# fVCO

View File

@ -32,7 +32,7 @@ class NXOSCA(LiteXModule):
clk_hf_freq_range = (1.76, 450e6)
clk_hf_freq = 450e6
def __init__(self):
def __init__(self, platform=None):
self.logger = logging.getLogger("NXOSCA")
self.logger.info("Creating NXOSCA.")
@ -40,6 +40,7 @@ class NXOSCA(LiteXModule):
self.hfsdc_clk_out = {}
self.lf_clk_out = None
self.params = {}
self.platform = platform
def create_hf_clk(self, cd, freq, margin=.05):
"""450 - 1.7 Mhz Clk"""
@ -86,21 +87,29 @@ class NXOSCA(LiteXModule):
def do_finalize(self):
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["p_HF_CLK_DIV"] = divisor
self.params["o_HFCLKOUT"] = self.hf_clk_out[0]
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:
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["p_HF_SED_SEC_DIV"] = divisor
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:
self.params["o_LFCLKOUT"] = self.lf_clk_out[0]
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)

291
litex/soc/cores/i2c.py Normal file
View File

@ -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))

View File

@ -79,10 +79,10 @@ class NXLRAM(LiteXModule):
wren = Signal()
self.comb += [
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,
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",

View File

@ -653,7 +653,10 @@ class VideoFrameBuffer(LiteXModule):
self.depth = depth = {
"rgb888" : 32,
"rgb565" : 16
"rgb565" : 16,
"rgb332" : 8,
"mono8" : 8,
"mono1" : 1,
}[format]
# # #
@ -728,16 +731,34 @@ class VideoFrameBuffer(LiteXModule):
if (depth == 32):
self.comb += [
source.r.eq(video_pipe_source.data[ 0: 8]),
source.g.eq(video_pipe_source.data[ 8:16]),
source.b.eq(video_pipe_source.data[16:24]),
source.r.eq(video_pipe_source.data[ 0: 8]),
source.g.eq(video_pipe_source.data[ 8:16]),
source.b.eq(video_pipe_source.data[16:24]),
]
else: # depth == 16
elif (depth == 16):
self.comb += [
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.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.
self.comb += self.underflow.eq(~source.valid)

View File

@ -1512,7 +1512,7 @@ class LiteXSoC(SoC):
self.add_config(name, identifier)
# 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.
from litex.soc.cores.uart import UART, UARTCrossover
@ -1529,8 +1529,9 @@ class LiteXSoC(SoC):
"usb_acm",
"serial(x)",
]
uart_pads_name = "serial" if uart_name == "sim" else uart_name
uart_pads = self.platform.request(uart_pads_name, loose=True)
if uart_pads is None:
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 = None
uart_kwargs = {

View File

@ -386,7 +386,7 @@ class PacketFIFO(LiteXModule):
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"])),
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),
]

View File

@ -15,35 +15,35 @@ import argparse
from migen import *
from litex.build.generic_platform import *
from litex.build.sim import SimPlatform
from litex.build.sim.config import SimConfig
from litex.build.sim import SimPlatform
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.builder 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 litex.soc.integration.builder import *
from litex.soc.integration.soc import *
from litedram import modules as litedram_modules
from litedram.modules import parse_spd_hexdump
from litex.soc.cores.bitbang import *
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 SDRAMPHYModel
from liteeth.phy.gmii import LiteEthPHYGMII
from liteeth.phy.xgmii import LiteEthPHYXGMII
from liteeth.phy.model import LiteEthPHYModel
from liteeth.mac import LiteEthMAC
from liteeth.core.arp import LiteEthARP
from liteeth.core.ip import LiteEthIP
from liteeth.core.udp import LiteEthUDP
from liteeth.core.icmp import LiteEthICMP
from liteeth.core import LiteEthUDPIPCore
from liteeth.common import *
from liteeth.phy.gmii import LiteEthPHYGMII
from liteeth.phy.xgmii import LiteEthPHYXGMII
from liteeth.phy.model import LiteEthPHYModel
from liteeth.mac import LiteEthMAC
from liteeth.core.arp import LiteEthARP
from liteeth.core.ip import LiteEthIP
from liteeth.core.udp import LiteEthUDP
from liteeth.core.icmp import LiteEthICMP
from liteeth.core import LiteEthUDPIPCore
from liteeth.frontend.etherbone import LiteEthEtherbone
from liteeth.common import *
from litex.soc.cores.video import VideoGenericPHY
from litescope import LiteScopeAnalyzer

226
test/test_i2c.py Executable file
View File

@ -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()