diff --git a/README.md b/README.md index becbbe4de..05cb4a61e 100644 --- a/README.md +++ b/README.md @@ -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: ``` diff --git a/litex/build/altera/common.py b/litex/build/altera/common.py index 3ed0947ba..d4a2c3c77 100644 --- a/litex/build/altera/common.py +++ b/litex/build/altera/common.py @@ -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, +} diff --git a/litex/build/altera/platform.py b/litex/build/altera/platform.py index e023d2ced..f7b3b0a8e 100644 --- a/litex/build/altera/platform.py +++ b/litex/build/altera/platform.py @@ -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, diff --git a/litex/build/efinix/common.py b/litex/build/efinix/common.py index 0197e2911..c52db5ee9 100644 --- a/litex/build/efinix/common.py +++ b/litex/build/efinix/common.py @@ -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([ diff --git a/litex/build/efinix/efinity.py b/litex/build/efinix/efinity.py index a7791bf96..0f782ac6b 100644 --- a/litex/build/efinix/efinity.py +++ b/litex/build/efinix/efinity.py @@ -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() diff --git a/litex/build/efinix/ifacewriter.py b/litex/build/efinix/ifacewriter.py index e5fbe9318..95cd66c8a 100644 --- a/litex/build/efinix/ifacewriter.py +++ b/litex/build/efinix/ifacewriter.py @@ -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"]): diff --git a/litex/build/gowin/gowin.py b/litex/build/gowin/gowin.py index 7129db38e..eef6b3cf3 100644 --- a/litex/build/gowin/gowin.py +++ b/litex/build/gowin/gowin.py @@ -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" diff --git a/litex/soc/cores/clock/efinix.py b/litex/soc/cores/clock/efinix.py index 63cca6839..ba2a9e70d 100644 --- a/litex/soc/cores/clock/efinix.py +++ b/litex/soc/cores/clock/efinix.py @@ -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 diff --git a/litex/soc/cores/clock/lattice_nx.py b/litex/soc/cores/clock/lattice_nx.py index 6702461f1..8d8126c9d 100644 --- a/litex/soc/cores/clock/lattice_nx.py +++ b/litex/soc/cores/clock/lattice_nx.py @@ -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) diff --git a/litex/soc/cores/i2c.py b/litex/soc/cores/i2c.py new file mode 100644 index 000000000..702b35c22 --- /dev/null +++ b/litex/soc/cores/i2c.py @@ -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 +# +# 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)) diff --git a/litex/soc/cores/ram/lattice_nx.py b/litex/soc/cores/ram/lattice_nx.py index 20eada5da..4eab1172d 100644 --- a/litex/soc/cores/ram/lattice_nx.py +++ b/litex/soc/cores/ram/lattice_nx.py @@ -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", diff --git a/litex/soc/cores/video.py b/litex/soc/cores/video.py index bb637b649..b9469280b 100644 --- a/litex/soc/cores/video.py +++ b/litex/soc/cores/video.py @@ -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) diff --git a/litex/soc/integration/soc.py b/litex/soc/integration/soc.py index 31d964032..6c286355f 100644 --- a/litex/soc/integration/soc.py +++ b/litex/soc/integration/soc.py @@ -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 = { diff --git a/litex/soc/interconnect/packet.py b/litex/soc/interconnect/packet.py index 123a587e1..762fcd8d9 100644 --- a/litex/soc/interconnect/packet.py +++ b/litex/soc/interconnect/packet.py @@ -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), ] diff --git a/litex/tools/litex_sim.py b/litex/tools/litex_sim.py index 1baa4ffb4..3407fc85d 100755 --- a/litex/tools/litex_sim.py +++ b/litex/tools/litex_sim.py @@ -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 diff --git a/test/test_i2c.py b/test/test_i2c.py new file mode 100755 index 000000000..37f72abda --- /dev/null +++ b/test/test_i2c.py @@ -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 +# +# 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()