forked from enjoy-digital/litex
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Fin Maaß <[email protected]>
- Loading branch information
1 parent
2c8d237
commit 2fc0398
Showing
6 changed files
with
921 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# | ||
# This file is part of LiteI2C | ||
# | ||
# Copyright (c) 2020 Antmicro <www.antmicro.com> | ||
# SPDX-License-Identifier: BSD-2-Clause | ||
|
||
from migen import * | ||
|
||
from litex.soc.integration.doc import AutoDoc | ||
from litex.soc.interconnect import wishbone, stream | ||
from litex.soc.interconnect.csr import * | ||
|
||
from litex.soc.cores.i2c.common import * | ||
from litex.soc.cores.i2c.crossbar import LiteI2CCrossbar | ||
from litex.soc.cores.i2c.master import LiteI2CMaster | ||
from litex.soc.cores.i2c.generic_phy import LiteI2CPHYCore | ||
|
||
|
||
class LiteI2CCore(Module): | ||
def __init__(self): | ||
self.source = stream.Endpoint(i2c_core2phy_layout) | ||
self.sink = stream.Endpoint(i2c_phy2core_layout) | ||
self.enable = Signal() | ||
|
||
|
||
class LiteI2C(Module, AutoCSR, AutoDoc): | ||
"""I2C Controller wrapper. | ||
The ``LiteI2C`` class provides a wrapper that can instantiate both ``LiteI2CMMAP`` and ``LiteI2CMaster`` and connect them to the PHY. | ||
Both options can be used at the same time with help of ``mux_sel`` register which allows to share access to PHY via crossbar. | ||
Parameters | ||
---------- | ||
phy : Module | ||
Module or object that contains PHY stream interfaces and a enable signal to connect the ``LiteI2CCore`` to. | ||
clk_freq : int | ||
Frequency of a clock connected to LiteI2C. | ||
clock_domain : str | ||
Name of LiteI2C clock domain. | ||
with_master : bool | ||
Enables register-operated I2C master controller. | ||
""" | ||
|
||
def __init__(self, sys_clk_freq, phy=None, pads=None, clock_domain="sys", | ||
with_master=True, master_tx_fifo_depth=1, master_rx_fifo_depth=1): | ||
|
||
if phy is None: | ||
if pads is None: | ||
raise ValueError("Either phy or pads must be provided.") | ||
self.submodules.phy = phy = LiteI2CPHYCore(pads, clock_domain, sys_clk_freq) | ||
|
||
|
||
self.submodules.crossbar = crossbar = LiteI2CCrossbar(clock_domain) | ||
|
||
self.comb += phy.enable.eq(crossbar.enable) | ||
|
||
if with_master: | ||
self.submodules.master = master = LiteI2CMaster( | ||
tx_fifo_depth = master_tx_fifo_depth, | ||
rx_fifo_depth = master_rx_fifo_depth) | ||
port_master = crossbar.get_port(master.enable) | ||
self.comb += [ | ||
port_master.source.connect(master.sink), | ||
master.source.connect(port_master.sink), | ||
] | ||
|
||
if clock_domain != "sys": | ||
self.comb += [ | ||
crossbar.tx_cdc.source.connect(phy.sink), | ||
phy.source.connect(crossbar.rx_cdc.sink), | ||
] | ||
else: | ||
self.comb += [ | ||
crossbar.master.source.connect(phy.sink), | ||
phy.source.connect(crossbar.master.sink), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# | ||
# This file is part of Litei2C | ||
# | ||
# Copyright (c) 2020 Antmicro <www.antmicro.com> | ||
# SPDX-License-Identifier: BSD-2-Clause | ||
|
||
from migen import * | ||
|
||
import math | ||
|
||
from litex.soc.integration.doc import AutoDoc, ModuleDoc | ||
|
||
from litex.build.io import SDRTristate | ||
|
||
|
||
def freq_to_div(sys_clk_freq, freq): | ||
return math.ceil(sys_clk_freq / (4*freq)) - 1 | ||
|
||
class LiteI2CClkGen(Module, AutoDoc): | ||
"""i2C Clock generator | ||
The ``Litei2CClkGen`` class provides a generic i2C clock generator. | ||
Parameters | ||
---------- | ||
pads : Object | ||
i2C pads description. | ||
device : str | ||
Device type for determining how to get output pin if it was not provided in pads. | ||
cnt_width : int | ||
Width of the internal counter ``cnt`` used for dividing the clock. | ||
with_ddr : bool | ||
Generate additional ``sample`` and ``update`` signals. | ||
Attributes | ||
---------- | ||
div : Signal(8), in | ||
Clock divisor, output clock frequency will be equal to ``sys_clk_freq/(4*(1+div))``. | ||
posedge : Signal(), out | ||
Outputs 1 when there is a rising edge on the generated clock, 0 otherwise. | ||
negedge : Signal(), out | ||
Outputs 1 when there is a falling edge on the generated clock, 0 otherwise. | ||
en : Signal(), in | ||
Clock enable input, output clock will be generated if set to 1, 0 resets the core. | ||
sample : Signal(), out | ||
Outputs 1 when ``sample_cnt==cnt``, can be used to sample incoming DDR data. | ||
sample_cnt : Signal(8), in | ||
Controls generation of the ``sample`` signal. | ||
update : Signal(), out | ||
Outputs 1 when ``update_cnt==cnt``, can be used to update outgoing DDR data. | ||
update_cnt : Signal(8), in | ||
Controls generation of the ``update`` signal. | ||
""" | ||
def __init__(self, pads, i2c_speed_mode, sys_clk_freq): | ||
self.posedge = posedge = Signal() | ||
self.negedge = negedge = Signal() | ||
self.tx = tx = Signal() | ||
self.rx = rx = Signal() | ||
self.en = en = Signal() | ||
self.keep_low = keep_low = Signal() | ||
|
||
cnt_width = bits_for(freq_to_div(sys_clk_freq, 100000)) | ||
|
||
div = Signal(cnt_width) | ||
cnt = Signal(cnt_width) | ||
sub_cnt = Signal(2) | ||
clk = Signal(reset=1) | ||
|
||
self.comb += [ | ||
Case(i2c_speed_mode, { | ||
0 : div.eq(freq_to_div(sys_clk_freq, 100000)), # 100kHz (Standard Mode) | ||
1 : div.eq(freq_to_div(sys_clk_freq, 400000)), # 400kHz (Fast Mode) | ||
2 : div.eq(freq_to_div(sys_clk_freq, 1000000)), # 1MHz (Fast Mode Plus) | ||
})] | ||
|
||
self.comb += [ | ||
posedge.eq(en & (sub_cnt == 2) & (cnt == div)), | ||
negedge.eq(en & (sub_cnt == 0) & (cnt == div)), | ||
tx.eq(en & (sub_cnt == 1) & (cnt == div)), | ||
rx.eq(en & (sub_cnt == 3) & (cnt == div)), | ||
] | ||
|
||
# Delayed edge to account for IO register delays. | ||
self.posedge_reg = posedge_reg = Signal() | ||
self.posedge_reg2 = posedge_reg2 = Signal() | ||
|
||
self.sync += [ | ||
posedge_reg.eq(posedge), | ||
posedge_reg2.eq(posedge_reg), | ||
] | ||
|
||
self.sync += [ | ||
If(en, | ||
If(cnt < div, | ||
cnt.eq(cnt+1), | ||
).Else( | ||
cnt.eq(0), | ||
clk.eq(sub_cnt[1]), | ||
If(sub_cnt < 3, | ||
sub_cnt.eq(sub_cnt+1), | ||
).Else( | ||
sub_cnt.eq(0), | ||
) | ||
) | ||
).Else( | ||
clk.eq(~keep_low), | ||
cnt.eq(0), | ||
sub_cnt.eq(0), | ||
) | ||
] | ||
|
||
self.specials += SDRTristate(pads.scl, | ||
o = Signal(), # I2C uses Pull-ups, only drive low. | ||
oe = ~clk, # Drive when scl is low. | ||
i = Signal(), # Not used. | ||
) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# | ||
# This file is part of Litei2c | ||
# | ||
# Copyright (c) 2020 Antmicro <www.antmicro.com> | ||
# SPDX-License-Identifier: BSD-2-Clause | ||
|
||
from migen import * | ||
from migen.genlib.cdc import MultiReg | ||
|
||
# Core <-> PHY Layouts ----------------------------------------------------------------------------- | ||
|
||
""" | ||
Stream layout for LiteI2CCore->PHY connection: | ||
data - flash byte address when cmd=1, data to transmit when cmd=2, unused in cmd=0 | ||
len - xfer length (in bits) | ||
width - xfer width (1/2/4/8) | ||
mask - dq output enable control (1 enables a output on a particular pin) | ||
""" | ||
i2c_core2phy_layout = [ | ||
("data", 32), | ||
("addr", 7), | ||
("len_tx", 3), | ||
("len_rx", 3) | ||
] | ||
""" | ||
Stream layout for PHY->LiteI2CCore connection | ||
data - 32-bits of data from flash | ||
""" | ||
i2c_phy2core_layout = [ | ||
("data", 32), | ||
("nack", 1), | ||
("unfinished_tx", 1), | ||
("unfinished_rx", 1) | ||
] | ||
|
||
MMAP_DEFAULT_TIMEOUT = 256 | ||
|
||
|
||
# Helpers ------------------------------------------------------------------------------------------ | ||
|
||
class ResyncReg(Module): | ||
def __init__(self, src, dst, clock_domain): | ||
if clock_domain == "sys": | ||
self.comb += dst.eq(src) | ||
else: | ||
self.specials += MultiReg(src, dst, clock_domain) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# | ||
# This file is part of LiteI2C | ||
# | ||
# Copyright (c) 2015 Florent Kermarrec <[email protected]> | ||
# Copyright (c) 2020 Antmicro <www.antmicro.com> | ||
# SPDX-License-Identifier: BSD-2-Clause | ||
|
||
from collections import OrderedDict | ||
|
||
from migen import * | ||
from migen.genlib.roundrobin import RoundRobin | ||
from litex.soc.cores.i2c.common import * | ||
|
||
from litex.soc.interconnect import stream | ||
|
||
|
||
class LiteI2CMasterPort: | ||
def __init__(self): | ||
self.source = stream.Endpoint(i2c_core2phy_layout) | ||
self.sink = stream.Endpoint(i2c_phy2core_layout) | ||
|
||
|
||
class LiteI2CSlavePort: | ||
def __init__(self): | ||
self.source = stream.Endpoint(i2c_phy2core_layout) | ||
self.sink = stream.Endpoint(i2c_core2phy_layout) | ||
|
||
|
||
class LiteI2CCrossbar(Module): | ||
def __init__(self, cd): | ||
self.cd = cd | ||
self.users = [] | ||
self.master = LiteI2CMasterPort() | ||
if cd != "sys": | ||
rx_cdc = stream.AsyncFIFO(i2c_phy2core_layout, 32, buffered=True) | ||
tx_cdc = stream.AsyncFIFO(i2c_core2phy_layout, 32, buffered=True) | ||
self.submodules.rx_cdc = ClockDomainsRenamer({"write": cd, "read": "sys"})(rx_cdc) | ||
self.submodules.tx_cdc = ClockDomainsRenamer({"write": "sys", "read": cd})(tx_cdc) | ||
self.comb += [ | ||
self.rx_cdc.source.connect(self.master.sink), | ||
self.master.source.connect(self.tx_cdc.sink), | ||
] | ||
|
||
self.enable = Signal() | ||
self.user_enable = [] | ||
self.user_request = [] | ||
|
||
def get_port(self, enable, request = None): | ||
user_port = LiteI2CSlavePort() | ||
internal_port = LiteI2CSlavePort() | ||
|
||
tx_stream = user_port.sink | ||
|
||
self.comb += tx_stream.connect(internal_port.sink) | ||
|
||
rx_stream = internal_port.source | ||
|
||
self.comb += rx_stream.connect(user_port.source) | ||
|
||
if request is None: | ||
request = Signal() | ||
self.comb += request.eq(enable) | ||
|
||
self.users.append(internal_port) | ||
self.user_enable.append(self.enable.eq(enable)) | ||
self.user_request.append(request) | ||
|
||
return user_port | ||
|
||
def do_finalize(self): | ||
self.submodules.rr = RoundRobin(len(self.users)) | ||
|
||
# TX | ||
self.submodules.tx_mux = tx_mux = stream.Multiplexer(i2c_core2phy_layout, len(self.users)) | ||
|
||
# RX | ||
self.submodules.rx_demux = rx_demux = stream.Demultiplexer(i2c_phy2core_layout, len(self.users)) | ||
|
||
for i, user in enumerate(self.users): | ||
self.comb += [ | ||
user.sink.connect(getattr(tx_mux, f"sink{i}")), | ||
getattr(rx_demux, f"source{i}").connect(user.source), | ||
] | ||
|
||
self.comb += [ | ||
self.rr.request.eq(Cat(self.user_request)), | ||
|
||
self.tx_mux.source.connect(self.master.source), | ||
self.tx_mux.sel.eq(self.rr.grant), | ||
|
||
self.master.sink.connect(self.rx_demux.sink), | ||
self.rx_demux.sel.eq(self.rr.grant), | ||
|
||
Case(self.rr.grant, dict(enumerate(self.user_enable))), | ||
] |
Oops, something went wrong.