text: #
  # This file is part of LiteX.
  #
  # Copyright (c) 2018-2019 Florent Kermarrec <florent@enjoy-digital.fr>
  # Copyright (c) 2020 Antmicro <www.antmicro.com>
  # SPDX-License-Identifier: BSD-2-Clause
  
  """AXI4 Full/Lite support for LiteX"""
  
  from migen import *
  from migen.genlib import roundrobin
  from migen.genlib.misc import split, displacer, chooser, WaitTimer
  
  from litex.soc.interconnect import stream
  from litex.build.generic_platform import *
  
  from litex.soc.interconnect import csr_bus
  
  # AXI Definition -----------------------------------------------------------------------------------
  
  BURST_FIXED    = 0b00
  BURST_INCR     = 0b01
  BURST_WRAP     = 0b10
  BURST_RESERVED = 0b11
  
  RESP_OKAY      = 0b00
  RESP_EXOKAY    = 0b01
  RESP_SLVERR    = 0b10
  RESP_DECERR    = 0b11
  
  def ax_description(address_width, id_width):
      return [
          ("addr",  address_width),
          ("burst", 2), # Burst type
          ("len",   8), # Number of data (-1) transfers (up to 256)
          ("size",  4), # Number of bytes (-1) of each data transfer (up to 1024 bits)
          ("lock",  2), # *
          ("prot",  3), # *
          ("cache", 4), # *
          ("qos",   4), # *
          ("id",    id_width)
      ]
      # * present for interconnect with others cores but not used by LiteX.
  
  def w_description(data_width, id_width):
      return [
          ("data", data_width),
          ("strb", data_width//8),
          ("id",   id_width)
      ]
  
  def b_description(id_width):
      return [
          ("resp", 2),
          ("id",   id_width)
      ]
  
  def r_description(data_width, id_width):
      return [
          ("resp", 2),
          ("data", data_width),
          ("id",   id_width)
      ]
  
  def _connect_axi(master, slave, keep=None, omit=None):
      channel_modes = {
          "aw": "master",
          "w" : "master",
          "b" : "slave",
          "ar": "master",
          "r" : "slave",
      }
      r = []
      for channel, mode in channel_modes.items():
          if mode == "master":
              m, s = getattr(master, channel), getattr(slave, channel)
          else:
              s, m = getattr(master, channel), getattr(slave, channel)
          r.extend(m.connect(s, keep=keep, omit=omit))
      return r
  
  def connect_to_pads(bus, pads, mode="master", axi_full=False):
      assert mode in ["slave", "master"]
      r = []
      def swap_mode(mode): return "master" if mode == "slave" else "slave"
      channel_modes = {
          "aw": mode,
          "w" : mode,
          "b" : swap_mode(mode),
          "ar": mode,
          "r" : swap_mode(mode),
      }
      for channel, mode in channel_modes.items():
          ch = getattr(bus, channel)
          sig_list = [("valid", 1)] + ch.description.payload_layout
          if ch in ["w", "r"] and axi_full:
              sig_list += [("last",  1)]
          for name, width in sig_list:
              sig  = getattr(ch, name)
              pad  = getattr(pads, channel + name)
              if mode == "master":
                  r.append(pad.eq(sig))
              else:
                  r.append(sig.eq(pad))
          for name, width in [("ready", 1)]:
              sig  = getattr(ch, name)
              pad  = getattr(pads, channel + name)
              if mode == "master":
                  r.append(sig.eq(pad))
              else:
                  r.append(pad.eq(sig))
      return r
  
  def _axi_layout_flat(axi):
      # yields tuples (channel, name, direction)
      def get_dir(channel, direction):
          if channel in ["b", "r"]:
              return {DIR_M_TO_S: DIR_S_TO_M, DIR_S_TO_M: DIR_M_TO_S}[direction]
          return direction
      for ch in ["aw", "w", "b", "ar", "r"]:
          channel = getattr(axi, ch)
          for group in channel.layout:
              if len(group) == 3:
                  name, _, direction = group
                  yield ch, name, get_dir(ch, direction)
              else:
                  _, subgroups = group
                  for subgroup in subgroups:
                      name, _, direction = subgroup
                      yield ch, name, get_dir(ch, direction)
  
  class AXIInterface:
      def __init__(self, data_width=32, address_width=32, id_width=1, clock_domain="sys"):
          self.data_width    = data_width
          self.address_width = address_width
          self.id_width      = id_width
          self.clock_domain  = clock_domain
  
          self.aw = stream.Endpoint(ax_description(address_width, id_width))
          self.w  = stream.Endpoint(w_description(data_width, id_width))
          self.b  = stream.Endpoint(b_description(id_width))
          self.ar = stream.Endpoint(ax_description(address_width, id_width))
          self.r  = stream.Endpoint(r_description(data_width, id_width))
  
      def connect_to_pads(self, pads, mode="master"):
          return connect_to_pads(self, pads, mode, axi_full=True)
  
      def get_ios(self, bus_name="wb"):
          subsignals = []
          for channel in ["aw", "w", "b", "ar", "r"]:
              for name in ["valid", "ready"] + (["last"] if channel in ["w", "r"] else []):
                  subsignals.append(Subsignal(channel + name, Pins(1)))
              for name, width in getattr(self, channel).description.payload_layout:
                  subsignals.append(Subsignal(channel + name, Pins(width)))
          ios = [(bus_name , 0) + tuple(subsignals)]
          return ios
  
      def connect(self, slave, **kwargs):
          return _connect_axi(self, slave, **kwargs)
  
      def layout_flat(self):
          return list(_axi_layout_flat(self))
  
  # AXI Lite Definition ------------------------------------------------------------------------------
  
  def ax_lite_description(address_width):
      return [("addr",  address_width)]
  
  def w_lite_description(data_width):
      return [
          ("data", data_width),
          ("strb", data_width//8)
      ]
  
  def b_lite_description():
      return [("resp", 2)]
  
  def r_lite_description(data_width):
      return [
          ("resp", 2),
          ("data", data_width)
      ]
  
  class AXILiteInterface:
      def __init__(self, data_width=32, address_width=32, clock_domain="sys", name=None):
          self.data_width    = data_width
          self.address_width = address_width
          self.clock_domain  = clock_domain
  
          self.aw = stream.Endpoint(ax_lite_description(address_width), name=name)
          self.w  = stream.Endpoint(w_lite_description(data_width), name=name)
          self.b  = stream.Endpoint(b_lite_description(), name=name)
          self.ar = stream.Endpoint(ax_lite_description(address_width), name=name)
          self.r  = stream.Endpoint(r_lite_description(data_width), name=name)
  
      def get_ios(self, bus_name="wb"):
          subsignals = []
          for channel in ["aw", "w", "b", "ar", "r"]:
              for name in ["valid", "ready"]:
                  subsignals.append(Subsignal(channel + name, Pins(1)))
              for name, width in getattr(self, channel).description.payload_layout:
                  subsignals.append(Subsignal(channel + name, Pins(width)))
          ios = [(bus_name , 0) + tuple(subsignals)]
          return ios
  
      def connect_to_pads(self, pads, mode="master"):
          return connect_to_pads(self, pads, mode)
  
      def connect(self, slave, **kwargs):
          return _connect_axi(self, slave, **kwargs)
  
      def layout_flat(self):
          return list(_axi_layout_flat(self))
  
      def write(self, addr, data, strb=None):
          if strb is None:
              strb = 2**len(self.w.strb) - 1
          # aw + w
          yield self.aw.valid.eq(1)
          yield self.aw.addr.eq(addr)
          yield self.w.data.eq(data)
          yield self.w.valid.eq(1)
          yield self.w.strb.eq(strb)
          yield
          while not (yield self.aw.ready):
              yield
          yield self.aw.valid.eq(0)
          yield self.aw.addr.eq(0)
          while not (yield self.w.ready):
              yield
          yield self.w.valid.eq(0)
          yield self.w.strb.eq(0)
          # b
          yield self.b.ready.eq(1)
          while not (yield self.b.valid):
              yield
          resp = (yield self.b.resp)
          yield self.b.ready.eq(0)
          return resp
  
      def read(self, addr):
          # ar
          yield self.ar.valid.eq(1)
          yield self.ar.addr.eq(addr)
          yield
          while not (yield self.ar.ready):
              yield
          yield self.ar.valid.eq(0)
          # r
          yield self.r.ready.eq(1)
          while not (yield self.r.valid):
              yield
          data = (yield self.r.data)
          resp = (yield self.r.resp)
          yield self.r.ready.eq(0)
          return (data, resp)
  
  # AXI Stream Definition ----------------------------------------------------------------------------
  
  class AXIStreamInterface(stream.Endpoint):
      def __init__(self, data_width=32, keep_width=0, user_width=0):
          self.data_width = data_width
          self.keep_width = keep_width
          self.user_width = user_width
          payload_layout = [("data", data_width)]
          if self.keep_width:
              payload_layout += [("keep", keep_width)]
          param_layout   = []
          if self.user_width:
              param_layout += [("user", user_width)]
          stream.Endpoint.__init__(self, stream.EndpointDescription(payload_layout, param_layout))
  
      def get_ios(self, bus_name="axi"):
          subsignals = [
              Subsignal("tvalid", Pins(1)),
              Subsignal("tlast",  Pins(1)),
              Subsignal("tready", Pins(1)),
              Subsignal("tdata",  Pins(self.data_width)),
          ]
          if self.keep_width:
              subsignals += [Subsignal("tkeep", Pins(self.keep_width))]
          if self.user_width:
              subsignals += [Subsignal("tuser", Pins(self.user_width))]
          ios = [(bus_name , 0) + tuple(subsignals)]
          return ios
  
      def connect_to_pads(self, pads, mode="master"):
          assert mode in ["slave", "master"]
          r = []
          if mode == "master":
              r.append(pads.tvalid.eq(self.valid))
              r.append(self.ready.eq(pads.tready))
              r.append(pads.tlast.eq(self.last))
              r.append(pads.tdata.eq(self.data))
              if self.keep_width:
                  r.append(pads.tkeep.eq(self.keep))
              if self.user_width:
                  r.append(pads.tuser.eq(self.user))
          if mode == "slave":
              r.append(self.valid.eq(pads.tvalid))
              r.append(pads.tready.eq(self.ready))
              r.append(self.last.eq(pads.tlast))
              r.append(self.data.eq(pads.tdata))
              if self.keep_width:
                  r.append(self.keep.eq(pads.tkeep))
              if self.user_width:
                  r.append(self.user.eq(pads.tuser))
          return r
  
  # AXI Bursts to Beats ------------------------------------------------------------------------------
  
  class AXIBurst2Beat(Module):
      def __init__(self, ax_burst, ax_beat, capabilities={BURST_FIXED, BURST_INCR, BURST_WRAP}):
          assert BURST_FIXED in capabilities
  
          # # #
  
          beat_count  = Signal(8)
          beat_size   = Signal(8 + 4)
          beat_offset = Signal(8 + 4)
          beat_wrap   = Signal(8 + 4)
  
          # Compute parameters
          self.comb += beat_size.eq(1 << ax_burst.size)
          self.comb += beat_wrap.eq(ax_burst.len << ax_burst.size)
  
          # Combinatorial logic
          self.comb += [
              ax_beat.valid.eq(ax_burst.valid | ~ax_beat.first),
              ax_beat.first.eq(beat_count == 0),
              ax_beat.last.eq(beat_count == ax_burst.len),
              ax_beat.addr.eq(ax_burst.addr + beat_offset),
              ax_beat.id.eq(ax_burst.id),
              If(ax_beat.ready,
                  If(ax_beat.last,
                      ax_burst.ready.eq(1)
                  )
              )
          ]
  
          # Synchronous logic
          self.sync += [
              If(ax_beat.valid & ax_beat.ready,
                  If(ax_beat.last,
                      beat_count.eq(0),
                      beat_offset.eq(0)
                  ).Else(
                      beat_count.eq(beat_count + 1),
                      If(((ax_burst.burst == BURST_INCR) & (BURST_INCR in capabilities)) |
                         ((ax_burst.burst == BURST_WRAP) & (BURST_WRAP in capabilities)),
                          beat_offset.eq(beat_offset + beat_size)
                      )
                  ),
                  If((ax_burst.burst == BURST_WRAP) & (BURST_WRAP in capabilities),
                      If(beat_offset == beat_wrap,
                          beat_offset.eq(0)
                      )
                  )
              )
          ]
  
  # AXI to AXI Lite ----------------------------------------------------------------------------------
  
  class AXI2AXILite(Module):
      # Note: Since this AXI bridge will mostly be used to target buses that are not supporting
      # simultaneous writes/reads, to reduce ressource usage the AXIBurst2Beat module is shared
      # between writes/reads.
      def __init__(self, axi, axi_lite):
          assert axi.data_width    == axi_lite.data_width
          assert axi.address_width == axi_lite.address_width
  
          ax_buffer = stream.Buffer(ax_description(axi.address_width, axi.id_width))
          ax_burst  = stream.Endpoint(ax_description(axi.address_width, axi.id_width))
          ax_beat   = stream.Endpoint(ax_description(axi.address_width, axi.id_width))
          self.comb += ax_burst.connect(ax_buffer.sink)
          ax_burst2beat = AXIBurst2Beat(ax_buffer.source, ax_beat)
          self.submodules += ax_buffer, ax_burst2beat
  
          _data         = Signal(axi.data_width)
          _cmd_done     = Signal()
          _last_ar_aw_n = Signal()
  
          self.submodules.fsm = fsm = FSM(reset_state="IDLE")
          fsm.act("IDLE",
              NextValue(_cmd_done, 0),
              If(axi.ar.valid & axi.aw.valid,
                  # If last access was a read, do a write
                  If(_last_ar_aw_n,
                      axi.aw.connect(ax_burst),
                      NextValue(_last_ar_aw_n, 0),
                      NextState("WRITE")
                  # If last access was a write, do a read
                  ).Else(
                      axi.ar.connect(ax_burst),
                      NextValue(_last_ar_aw_n, 1),
                      NextState("READ"),
                  )
              ).Elif(axi.ar.valid,
                  axi.ar.connect(ax_burst),
                  NextValue(_last_ar_aw_n, 1),
                  NextState("READ"),
              ).Elif(axi.aw.valid,
                  axi.aw.connect(ax_burst),
                  NextValue(_last_ar_aw_n, 0),
                  NextState("WRITE")
              )
          )
          fsm.act("READ",
              # ar (read command)
              axi_lite.ar.valid.eq(ax_beat.valid & ~_cmd_done),
              axi_lite.ar.addr.eq(ax_beat.addr),
              ax_beat.ready.eq(axi_lite.ar.ready & ~_cmd_done),
              If(ax_beat.valid & ax_beat.last,
                  If(axi_lite.ar.ready,
                      ax_beat.ready.eq(0),
                      NextValue(_cmd_done, 1)
                  )
              ),
              # r (read data & response)
              axi.r.valid.eq(axi_lite.r.valid),
              axi.r.last.eq(_cmd_done),
              axi.r.resp.eq(RESP_OKAY),
              axi.r.id.eq(ax_beat.id),
              axi.r.data.eq(axi_lite.r.data),
              axi_lite.r.ready.eq(axi.r.ready),
              # Exit
              If(axi.r.valid & axi.r.last & axi.r.ready,
                  ax_beat.ready.eq(1),
                  NextState("IDLE")
              )
          )
          # Always accept write responses.
          self.comb += axi_lite.b.ready.eq(1)
          fsm.act("WRITE",
              # aw (write command)
              axi_lite.aw.valid.eq(ax_beat.valid & ~_cmd_done),
              axi_lite.aw.addr.eq(ax_beat.addr),
              ax_beat.ready.eq(axi_lite.aw.ready & ~_cmd_done),
              If(ax_beat.valid & ax_beat.last,
                  If(axi_lite.aw.ready,
                      ax_beat.ready.eq(0),
                      NextValue(_cmd_done, 1)
                  )
              ),
              # w (write data)
              axi_lite.w.valid.eq(axi.w.valid),
              axi_lite.w.data.eq(axi.w.data),
              axi_lite.w.strb.eq(axi.w.strb),
              axi.w.ready.eq(axi_lite.w.ready),
              # Exit
              If(axi.w.valid & axi.w.last & axi.w.ready,
                  NextState("WRITE-RESP")
              )
          )
          fsm.act("WRITE-RESP",
              axi.b.valid.eq(1),
              axi.b.resp.eq(RESP_OKAY),
              axi.b.id.eq(ax_beat.id),
              If(axi.b.ready,
                  ax_beat.ready.eq(1),
                  NextState("IDLE")
              )
          )
  
  # AXI Lite to AXI ----------------------------------------------------------------------------------
  
  class AXILite2AXI(Module):
      def __init__(self, axi_lite, axi, write_id=0, read_id=0, prot=0, burst_type="INCR"):
          assert isinstance(axi_lite, AXILiteInterface)
          assert isinstance(axi, AXIInterface)
          assert axi_lite.data_width == axi.data_width
          assert axi_lite.address_width == axi.address_width
  
          # n bytes, encoded as log2(n)
          burst_size = log2_int(axi.data_width // 8)
          # Burst type has no meaning as we use burst length of 1, but AXI slaves may require certain
          # type of bursts, so it is probably safest to use INCR in general.
          burst_type = {
              "FIXED": 0b00,
              "INCR":  0b01,
              "WRAP":  0b10,
          }[burst_type]
  
          self.comb += [
              # aw (write command)
              axi.aw.valid.eq(axi_lite.aw.valid),
              axi_lite.aw.ready.eq(axi.aw.ready),
              axi.aw.addr.eq(axi_lite.aw.addr),
              axi.aw.burst.eq(burst_type),
              axi.aw.len.eq(0),  # 1 transfer per burst
              axi.aw.size.eq(burst_size),
              axi.aw.lock.eq(0),  # Normal access
              axi.aw.prot.eq(prot),
              axi.aw.cache.eq(0b0011),  # Normal Non-cacheable Bufferable
              axi.aw.qos.eq(0),
              axi.aw.id.eq(write_id),
  
              # w (write data)
              axi.w.valid.eq(axi_lite.w.valid),
              axi_lite.w.ready.eq(axi.w.ready),
              axi.w.data.eq(axi_lite.w.data),
              axi.w.strb.eq(axi_lite.w.strb),
              axi.w.last.eq(1),
  
              # b (write response)
              axi_lite.b.valid.eq(axi.b.valid),
              axi_lite.b.resp.eq(axi.b.resp),
              axi.b.ready.eq(axi_lite.b.ready),
  
              # ar (read command)
              axi.ar.valid.eq(axi_lite.ar.valid),
              axi_lite.ar.ready.eq(axi.ar.ready),
              axi.ar.addr.eq(axi_lite.ar.addr),
              axi.ar.burst.eq(burst_type),
              axi.ar.len.eq(0),
              axi.ar.size.eq(burst_size),
              axi.ar.lock.eq(0),
              axi.ar.prot.eq(prot),
              axi.ar.cache.eq(0b0011),
              axi.ar.qos.eq(0),
              axi.ar.id.eq(read_id),
  
              # r (read response & data)
              axi_lite.r.valid.eq(axi.r.valid),
              axi_lite.r.resp.eq(axi.r.resp),
              axi_lite.r.data.eq(axi.r.data),
              axi.r.ready.eq(axi_lite.r.ready),
          ]
  
  # AXI Lite to Wishbone -----------------------------------------------------------------------------
  
  class AXILite2Wishbone(Module):
      def __init__(self, axi_lite, wishbone, base_address=0x00000000):
          wishbone_adr_shift = log2_int(axi_lite.data_width//8)
          assert axi_lite.data_width    == len(wishbone.dat_r)
          assert axi_lite.address_width == len(wishbone.adr) + wishbone_adr_shift
  
          _data         = Signal(axi_lite.data_width)
          _r_addr       = Signal(axi_lite.address_width)
          _w_addr       = Signal(axi_lite.address_width)
          _last_ar_aw_n = Signal()
          self.comb += _r_addr.eq(axi_lite.ar.addr - base_address)
          self.comb += _w_addr.eq(axi_lite.aw.addr - base_address)
  
          self.submodules.fsm = fsm = FSM(reset_state="IDLE")
          fsm.act("IDLE",
              If(axi_lite.ar.valid & axi_lite.aw.valid,
                  # If last access was a read, do a write
                  If(_last_ar_aw_n,
                      NextValue(_last_ar_aw_n, 0),
                      NextState("DO-WRITE")
                  # If last access was a write, do a read
                  ).Else(
                      NextValue(_last_ar_aw_n, 1),
                      NextState("DO-READ")
                  )
              ).Elif(axi_lite.ar.valid,
                  NextValue(_last_ar_aw_n, 1),
                  NextState("DO-READ")
              ).Elif(axi_lite.aw.valid,
                  NextValue(_last_ar_aw_n, 0),
                  NextState("DO-WRITE")
              )
          )
          fsm.act("DO-READ",
              wishbone.stb.eq(1),
              wishbone.cyc.eq(1),
              wishbone.adr.eq(_r_addr[wishbone_adr_shift:]),
              wishbone.sel.eq(2**len(wishbone.sel) - 1),
              If(wishbone.ack,
                  axi_lite.ar.ready.eq(1),
                  NextValue(_data, wishbone.dat_r),
                  NextState("SEND-READ-RESPONSE")
              )
          )
          fsm.act("SEND-READ-RESPONSE",
              axi_lite.r.valid.eq(1),
              axi_lite.r.resp.eq(RESP_OKAY),
              axi_lite.r.data.eq(_data),
              If(axi_lite.r.ready,
                  NextState("IDLE")
              )
          )
          fsm.act("DO-WRITE",
              wishbone.stb.eq(axi_lite.w.valid),
              wishbone.cyc.eq(axi_lite.w.valid),
              wishbone.we.eq(1),
              wishbone.adr.eq(_w_addr[wishbone_adr_shift:]),
              wishbone.sel.eq(axi_lite.w.strb),
              wishbone.dat_w.eq(axi_lite.w.data),
              If(wishbone.ack,
                  axi_lite.aw.ready.eq(1),
                  axi_lite.w.ready.eq(1),
                  NextState("SEND-WRITE-RESPONSE")
              )
          )
          fsm.act("SEND-WRITE-RESPONSE",
              axi_lite.b.valid.eq(1),
              axi_lite.b.resp.eq(RESP_OKAY),
              If(axi_lite.b.ready,
                  NextState("IDLE")
              )
          )
  
  # AXI to Wishbone ----------------------------------------------------------------------------------
  
  class AXI2Wishbone(Module):
      def __init__(self, axi, wishbone, base_address=0x00000000):
          axi_lite          = AXILiteInterface(axi.data_width, axi.address_width)
          axi2axi_lite      = AXI2AXILite(axi, axi_lite)
          axi_lite2wishbone = AXILite2Wishbone(axi_lite, wishbone, base_address)
          self.submodules += axi2axi_lite, axi_lite2wishbone
  
  # Wishbone to AXILite ------------------------------------------------------------------------------
  
  class Wishbone2AXILite(Module):
      def __init__(self, wishbone, axi_lite, base_address=0x00000000):
          wishbone_adr_shift = log2_int(axi_lite.data_width//8)
          assert axi_lite.data_width    == len(wishbone.dat_r)
          assert axi_lite.address_width == len(wishbone.adr) + wishbone_adr_shift
  
          _cmd_done  = Signal()
          _data_done = Signal()
          _addr      = Signal(len(wishbone.adr))
          self.comb += _addr.eq(wishbone.adr - base_address//4)
  
          self.submodules.fsm = fsm = FSM(reset_state="IDLE")
          fsm.act("IDLE",
              NextValue(_cmd_done,  0),
              NextValue(_data_done, 0),
              If(wishbone.stb & wishbone.cyc,
                  If(wishbone.we,
                      NextState("WRITE")
                  ).Else(
                      NextState("READ")
                  )
              )
          )
          fsm.act("WRITE",
              # aw (write command)
              axi_lite.aw.valid.eq(~_cmd_done),
              axi_lite.aw.addr[wishbone_adr_shift:].eq(_addr),
              If(axi_lite.aw.valid & axi_lite.aw.ready,
                  NextValue(_cmd_done, 1)
              ),
              # w (write data)
              axi_lite.w.valid.eq(~_data_done),
              axi_lite.w.data.eq(wishbone.dat_w),
              axi_lite.w.strb.eq(wishbone.sel),
              If(axi_lite.w.valid & axi_lite.w.ready,
                  NextValue(_data_done, 1),
              ),
              # b (write response)
              axi_lite.b.ready.eq(_cmd_done & _data_done),
              If(axi_lite.b.valid & axi_lite.b.ready,
                  If(axi_lite.b.resp == RESP_OKAY,
                      wishbone.ack.eq(1),
                      NextState("IDLE")
                  ).Else(
                      NextState("ERROR")
                  )
              )
          )
          fsm.act("READ",
              # ar (read command)
              axi_lite.ar.valid.eq(~_cmd_done),
              axi_lite.ar.addr[wishbone_adr_shift:].eq(_addr),
              If(axi_lite.ar.valid & axi_lite.ar.ready,
                  NextValue(_cmd_done, 1)
              ),
              # r (read data & response)
              axi_lite.r.ready.eq(_cmd_done),
              If(axi_lite.r.valid & axi_lite.r.ready,
                  If(axi_lite.r.resp == RESP_OKAY,
                      wishbone.dat_r.eq(axi_lite.r.data),
                      wishbone.ack.eq(1),
                      NextState("IDLE"),
                  ).Else(
                      NextState("ERROR")
                  )
              )
          )
          fsm.act("ERROR",
              wishbone.ack.eq(1),
              wishbone.err.eq(1),
              NextState("IDLE")
          )
  
  # Wishbone to AXI ----------------------------------------------------------------------------------
  
  class Wishbone2AXI(Module):
      def __init__(self, wishbone, axi, base_address=0x00000000):
          axi_lite          = AXILiteInterface(axi.data_width, axi.address_width)
          wishbone2axi_lite = Wishbone2AXILite(wishbone, axi_lite, base_address)
          axi_lite2axi      = AXILite2AXI(axi_lite, axi)
          self.submodules += wishbone2axi_lite, axi_lite2axi
  
  # AXILite to CSR -----------------------------------------------------------------------------------
  
  def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=None):
      """Connection of AXILite to simple bus with 1-cycle latency, such as CSR bus or Memory port"""
      bus_data_width = axi_lite.data_width
      adr_shift      = log2_int(bus_data_width//8)
      do_read        = Signal()
      do_write       = Signal()
      last_was_read  = Signal()
  
      comb = []
      if port_dat_w is not None:
          comb.append(port_dat_w.eq(axi_lite.w.data))
      if port_we is not None:
          if len(port_we) > 1:
              for i in range(bus_data_width//8):
                  comb.append(port_we[i].eq(axi_lite.w.valid & axi_lite.w.ready & axi_lite.w.strb[i]))
          else:
              comb.append(port_we.eq(axi_lite.w.valid & axi_lite.w.ready & (axi_lite.w.strb != 0)))
  
      fsm = FSM()
      fsm.act("START-TRANSACTION",
          # If the last access was a read, do a write, and vice versa.
          If(axi_lite.aw.valid & axi_lite.ar.valid,
              do_write.eq(last_was_read),
              do_read.eq(~last_was_read),
          ).Else(
              do_write.eq(axi_lite.aw.valid),
              do_read.eq(axi_lite.ar.valid),
          ),
          # Start reading/writing immediately not to waste a cycle.
          If(do_write,
              port_adr.eq(axi_lite.aw.addr[adr_shift:]),
              If(axi_lite.w.valid,
                  axi_lite.aw.ready.eq(1),
                  axi_lite.w.ready.eq(1),
                  NextState("SEND-WRITE-RESPONSE")
              )
          ).Elif(do_read,
              port_adr.eq(axi_lite.ar.addr[adr_shift:]),
              axi_lite.ar.ready.eq(1),
              NextState("SEND-READ-RESPONSE"),
          )
      )
      fsm.act("SEND-READ-RESPONSE",
          NextValue(last_was_read, 1),
          # As long as we have correct address port.dat_r will be valid.
          port_adr.eq(axi_lite.ar.addr[adr_shift:]),
          axi_lite.r.data.eq(port_dat_r),
          axi_lite.r.resp.eq(RESP_OKAY),
          axi_lite.r.valid.eq(1),
          If(axi_lite.r.ready,
              NextState("START-TRANSACTION")
          )
      )
      fsm.act("SEND-WRITE-RESPONSE",
          NextValue(last_was_read, 0),
          axi_lite.b.valid.eq(1),
          axi_lite.b.resp.eq(RESP_OKAY),
          If(axi_lite.b.ready,
              NextState("START-TRANSACTION")
          )
      )
      return fsm, comb
  
  class AXILite2CSR(Module):
      def __init__(self, axi_lite=None, bus_csr=None, register=False):
          # TODO: unused register argument
          if axi_lite is None:
              axi_lite = AXILiteInterface()
          if bus_csr is None:
              bus_csr = csr_bus.Interface()
  
          self.axi_lite = axi_lite
          self.csr      = bus_csr
  
          fsm, comb = axi_lite_to_simple(
              axi_lite   = self.axi_lite,
              port_adr   = self.csr.adr,
              port_dat_r = self.csr.dat_r,
              port_dat_w = self.csr.dat_w,
              port_we    = self.csr.we)
          self.submodules.fsm = fsm
          self.comb += comb
  
  # AXILite SRAM -------------------------------------------------------------------------------------
  
  class AXILiteSRAM(Module):
      def __init__(self, mem_or_size, read_only=None, init=None, bus=None):
          if bus is None:
              bus = AXILiteInterface()
          self.bus = bus
  
          bus_data_width = len(self.bus.r.data)
          if isinstance(mem_or_size, Memory):
              assert(mem_or_size.width <= bus_data_width)
              self.mem = mem_or_size
          else:
              self.mem = Memory(bus_data_width, mem_or_size//(bus_data_width//8), init=init)
  
          if read_only is None:
              if hasattr(self.mem, "bus_read_only"):
                  read_only = self.mem.bus_read_only
              else:
                  read_only = False
  
          # # #
  
          # Create memory port
          port = self.mem.get_port(write_capable=not read_only, we_granularity=8,
              mode=READ_FIRST if read_only else WRITE_FIRST)
          self.specials += self.mem, port
  
          # Generate write enable signal
          if not read_only:
              self.comb += port.dat_w.eq(self.bus.w.data),
              self.comb += [port.we[i].eq(self.bus.w.valid & self.bus.w.ready & self.bus.w.strb[i])
                  for i in range(bus_data_width//8)]
  
          # Transaction logic
          fsm, comb = axi_lite_to_simple(
              axi_lite   = self.bus,
              port_adr   = port.adr,
              port_dat_r = port.dat_r,
              port_dat_w = port.dat_w if not read_only else None,
              port_we    = port.we if not read_only else None)
          self.submodules.fsm = fsm
          self.comb += comb
  
  # AXILite Data Width Converter ---------------------------------------------------------------------
  
  class _AXILiteDownConverterWrite(Module):
      def __init__(self, master, slave):
          assert isinstance(master, AXILiteInterface) and isinstance(slave, AXILiteInterface)
          dw_from      = len(master.w.data)
          dw_to        = len(slave.w.data)
          ratio        = dw_from//dw_to
  
          skip         = Signal()
          counter      = Signal(max=ratio)
          aw_ready     = Signal()
          w_ready      = Signal()
          resp         = Signal.like(master.b.resp)
  
          # # #
  
          # Data path
          self.comb += [
              slave.aw.addr.eq(master.aw.addr + counter*(dw_to//8)),
              Case(counter, {i: slave.w.data.eq(master.w.data[i*dw_to:]) for i in range(ratio)}),
              Case(counter, {i: slave.w.strb.eq(master.w.strb[i*dw_to//8:]) for i in range(ratio)}),
              master.b.resp.eq(resp),
          ]
  
          # Control Path
          fsm = FSM(reset_state="IDLE")
          fsm = ResetInserter()(fsm)
          self.submodules.fsm = fsm
          # Reset the converter state if master breaks a request, we can do that as
          # aw.valid and w.valid are kept high in CONVERT and RESPOND-SLAVE, and
          # acknowledged only when moving to RESPOND-MASTER, and then b.valid is 1.
          self.comb += fsm.reset.eq(~((master.aw.valid | master.w.valid) | master.b.valid))
  
          fsm.act("IDLE",
              NextValue(counter, 0),
              NextValue(resp, RESP_OKAY),
              If(master.aw.valid & master.w.valid,
                  NextState("CONVERT")
              )
          )
          fsm.act("CONVERT",
              skip.eq(slave.w.strb == 0),
              slave.aw.valid.eq(~skip & ~aw_ready),
              slave.w.valid.eq(~skip & ~w_ready),
              If(slave.aw.ready,
                  NextValue(aw_ready, 1)
              ),
              If(slave.w.ready,
                  NextValue(w_ready, 1)
              ),
              # When skipping, we just increment the counter.
              If(skip,
                  NextValue(counter, counter + 1),
                  # Corner-case: when the last word is being skipped, we must send the response.
                  If(counter == (ratio - 1),
                      master.aw.ready.eq(1),
                      master.w.ready.eq(1),
                      NextState("RESPOND-MASTER")
                  )
              # Write current word and wait for write response.
              ).Elif((slave.aw.ready | aw_ready) & (slave.w.ready | w_ready),
                  NextState("RESPOND-SLAVE")
              )
          )
          fsm.act("RESPOND-SLAVE",
              NextValue(aw_ready, 0),
              NextValue(w_ready, 0),
              If(slave.b.valid,
                  slave.b.ready.eq(1),
                  # Errors are sticky, so the first one is always sent.
                  If((resp == RESP_OKAY) & (slave.b.resp != RESP_OKAY),
                      NextValue(resp, slave.b.resp)
                  ),
                  If(counter == (ratio - 1),
                      master.aw.ready.eq(1),
                      master.w.ready.eq(1),
                      NextState("RESPOND-MASTER")
                  ).Else(
                      NextValue(counter, counter + 1),
                      NextState("CONVERT")
                  )
              )
          )
          fsm.act("RESPOND-MASTER",
              NextValue(aw_ready, 0),
              NextValue(w_ready, 0),
              master.b.valid.eq(1),
              If(master.b.ready,
                  NextState("IDLE")
              )
          )
  
  class _AXILiteDownConverterRead(Module):
      def __init__(self, master, slave):
          assert isinstance(master, AXILiteInterface) and isinstance(slave, AXILiteInterface)
          dw_from      = len(master.r.data)
          dw_to        = len(slave.r.data)
          ratio        = dw_from//dw_to
  
          skip         = Signal()
          counter      = Signal(max=ratio)
          resp         = Signal.like(master.r.resp)
  
          # # #
  
          # Data path
          # Shift the data word
          r_data = Signal(dw_from, reset_less=True)
          self.sync += If(slave.r.ready, r_data.eq(master.r.data))
          self.comb += master.r.data.eq(Cat(r_data[dw_to:], slave.r.data))
          # Connect address, resp
          self.comb += [
              slave.ar.addr.eq(master.ar.addr + counter*(dw_to//8)),
              master.r.resp.eq(resp),
          ]
  
          # Control Path
          fsm = FSM(reset_state="IDLE")
          fsm = ResetInserter()(fsm)
          self.submodules.fsm = fsm
          # Reset the converter state if master breaks a request, we can do that as
          # ar.valid is high in CONVERT and RESPOND-SLAVE, and r.valid in RESPOND-MASTER.
          self.comb += fsm.reset.eq(~(master.ar.valid | master.r.valid))
  
          fsm.act("IDLE",
              NextValue(counter, 0),
              NextValue(resp, RESP_OKAY),
              If(master.ar.valid,
                  NextState("CONVERT")
              )
          )
          fsm.act("CONVERT",
              slave.ar.valid.eq(1),
              If(slave.ar.ready,
                  NextState("RESPOND-SLAVE")
              )
          )
          fsm.act("RESPOND-SLAVE",
              If(slave.r.valid,
                  # Errors are sticky, so the first one is always sent.
                  If((resp == RESP_OKAY) & (slave.r.resp != RESP_OKAY),
                      NextValue(resp, slave.r.resp)
                  ),
                  # On last word acknowledge ar and hold slave.r.valid until we get master.r.ready.
                  If(counter == (ratio - 1),
                      master.ar.ready.eq(1),
                      NextState("RESPOND-MASTER")
                  # Acknowledge the response and continue conversion.
                  ).Else(
                      slave.r.ready.eq(1),
                      NextValue(counter, counter + 1),
                      NextState("CONVERT")
                  )
              )
          )
          fsm.act("RESPOND-MASTER",
              master.r.valid.eq(1),
              If(master.r.ready,
                  slave.r.ready.eq(1),
                  NextState("IDLE")
              )
          )
  
  class AXILiteDownConverter(Module):
      def __init__(self, master, slave):
          self.submodules.write = _AXILiteDownConverterWrite(master, slave)
          self.submodules.read  = _AXILiteDownConverterRead(master, slave)
  
  class AXILiteUpConverter(Module):
      # TODO: we could try joining multiple master accesses into single slave access would require
      # checking if address changes and a way to flush on single access
      def __init__(self, master, slave):
          assert isinstance(master, AXILiteInterface) and isinstance(slave, AXILiteInterface)
          dw_from      = len(master.r.data)
          dw_to        = len(slave.r.data)
          ratio        = dw_to//dw_from
          master_align = log2_int(master.data_width//8)
          slave_align  = log2_int(slave.data_width//8)
  
          wr_word   = Signal(log2_int(ratio))
          rd_word   = Signal(log2_int(ratio))
          wr_word_r = Signal(log2_int(ratio))
          rd_word_r = Signal(log2_int(ratio))
  
          # # #
  
          self.comb += master.connect(slave, omit={"addr", "strb", "data"})
  
          # Address
          self.comb += [
              slave.aw.addr[slave_align:].eq(master.aw.addr[slave_align:]),
              slave.ar.addr[slave_align:].eq(master.ar.addr[slave_align:]),
          ]
  
          # Data path
          wr_cases, rd_cases = {}, {}
          for i in range(ratio):
              strb_from = i     * dw_from//8
              strb_to   = (i+1) * dw_from//8
              data_from = i     * dw_from
              data_to   = (i+1) * dw_from
              wr_cases[i] = [
                  slave.w.strb[strb_from:strb_to].eq(master.w.strb),
                  slave.w.data[data_from:data_to].eq(master.w.data),
              ]
              rd_cases[i] = [
                  master.r.data.eq(slave.r.data[data_from:data_to]),
              ]
  
          # Switch current word based on the last valid master address.
          self.sync += If(master.aw.valid, wr_word_r.eq(wr_word))
          self.sync += If(master.ar.valid, rd_word_r.eq(rd_word))
          self.comb += [
              Case(master.aw.valid, {
                  0: wr_word.eq(wr_word_r),
                  1: wr_word.eq(master.aw.addr[master_align:slave_align]),
              }),
              Case(master.ar.valid, {
                  0: rd_word.eq(rd_word_r),
                  1: rd_word.eq(master.ar.addr[master_align:slave_align]),
              }),
          ]
  
          self.comb += Case(wr_word, wr_cases)
          self.comb += Case(rd_word, rd_cases)
  
  class AXILiteConverter(Module):
      """AXILite data width converter"""
      def __init__(self, master, slave):
          self.master = master
          self.slave = slave
  
          # # #
  
          dw_from = len(master.r.data)
          dw_to   = len(slave.r.data)
          ratio   = dw_from/dw_to
  
          if ratio > 1:
              self.submodules += AXILiteDownConverter(master, slave)
          elif ratio < 1:
              self.submodules += AXILiteUpConverter(master, slave)
          else:
              self.comb += master.connect(slave)
  
  # AXILite Clock Domain Crossing --------------------------------------------------------------------
  
  class AXILiteClockDomainCrossing(Module):
      """AXILite Clock Domain Crossing"""
      def __init__(self, master, slave, cd_from="sys", cd_to="sys"):
          # Same Clock Domain, direct connection.
          if cd_from == cd_to:
              self.comb += [
                  # Write.
                  master.aw.connect(slave.aw),
                  master.w.connect(slave.w),
                  slave.b.connect(master.b),
                  # Read.
                  master.ar.connect(slave.ar),
                  slave.r.connect(master.r),
              ]
          # Clock Domain Crossing.
          else:
              # Write.
              aw_cdc = stream.ClockDomainCrossing(master.aw.description, cd_from,   cd_to)
              w_cdc  = stream.ClockDomainCrossing(master.w.description,  cd_from,   cd_to)
              b_cdc  = stream.ClockDomainCrossing(master.b.description,    cd_to, cd_from)
              self.submodules += aw_cdc, w_cdc, b_cdc
              self.comb += [
                  master.aw.connect(aw_cdc.sink),
                  aw_cdc.source.connect(slave.aw),
                  master.w.connect(w_cdc.sink),
                  w_cdc.source.connect(slave.w),
                  slave.b.connect(b_cdc.sink),
                  b_cdc.source.connect(master.b),
              ]
              # Read.
              ar_cdc = stream.ClockDomainCrossing(master.ar.description, cd_from,   cd_to)
              r_cdc  = stream.ClockDomainCrossing(master.r.description,    cd_to, cd_from)
              self.submodules += ar_cdc, r_cdc
              self.comb += [
                  master.ar.connect(ar_cdc.sink),
                  ar_cdc.source.connect(slave.ar),
                  slave.r.connect(r_cdc.sink),
                  r_cdc.source.connect(master.r),
              ]
  
  # AXILite Timeout ----------------------------------------------------------------------------------
  
  class AXILiteTimeout(Module):
      """Protect master against slave timeouts (master _has_ to respond correctly)"""
      def __init__(self, master, cycles):
          self.error = Signal()
          wr_error   = Signal()
          rd_error   = Signal()
  
          # # #
  
          self.comb += self.error.eq(wr_error | rd_error)
  
          wr_timer = WaitTimer(int(cycles))
          rd_timer = WaitTimer(int(cycles))
          self.submodules += wr_timer, rd_timer
  
          def channel_fsm(timer, wait_cond, error, response):
              fsm = FSM(reset_state="WAIT")
              fsm.act("WAIT",
                  timer.wait.eq(wait_cond),
                  # done is updated in `sync`, so we must make sure that `ready` has not been issued
                  # by slave during that single cycle, by checking `timer.wait`.
                  If(timer.done & timer.wait,
                      error.eq(1),
                      NextState("RESPOND")
                  )
              )
              fsm.act("RESPOND", *response)
              return fsm
  
          self.submodules.wr_fsm = channel_fsm(
              timer     = wr_timer,
              wait_cond = (master.aw.valid & ~master.aw.ready) | (master.w.valid & ~master.w.ready),
              error     = wr_error,
              response  = [
                  master.aw.ready.eq(master.aw.valid),
                  master.w.ready.eq(master.w.valid),
                  master.b.valid.eq(~master.aw.valid & ~master.w.valid),
                  master.b.resp.eq(RESP_SLVERR),
                  If(master.b.valid & master.b.ready,
                      NextState("WAIT")
                  )
              ])
  
          self.submodules.rd_fsm = channel_fsm(
              timer     = rd_timer,
              wait_cond = master.ar.valid & ~master.ar.ready,
              error     = rd_error,
              response  = [
                  master.ar.ready.eq(master.ar.valid),
                  master.r.valid.eq(~master.ar.valid),
                  master.r.resp.eq(RESP_SLVERR),
                  master.r.data.eq(2**len(master.r.data) - 1),
                  If(master.r.valid & master.r.ready,
                      NextState("WAIT")
                  )
              ])
  
  # AXILite Interconnect -----------------------------------------------------------------------------
  
  class _AXILiteRequestCounter(Module):
      def __init__(self, request, response, max_requests=256):
          self.counter = counter = Signal(max=max_requests)
          self.full = full = Signal()
          self.empty = empty = Signal()
          self.stall = stall = Signal()
          self.ready = self.empty
  
          self.comb += [
              full.eq(counter == max_requests - 1),
              empty.eq(counter == 0),
              stall.eq(request & full),
          ]
  
          self.sync += [
              If(request & response,
                  counter.eq(counter)
              ).Elif(request & ~full,
                  counter.eq(counter + 1)
              ).Elif(response & ~empty,
                  counter.eq(counter - 1)
              ),
          ]
  
  class AXILiteInterconnectPointToPoint(Module):
      def __init__(self, master, slave):
          self.comb += master.connect(slave)
  
  class AXILiteArbiter(Module):
      """AXI Lite arbiter
  
      Arbitrate between master interfaces and connect one to the target. New master will not be
      selected until all requests have been responded to. Arbitration for write and read channels is
      done separately.
      """
      def __init__(self, masters, target):
          self.submodules.rr_write = roundrobin.RoundRobin(len(masters), roundrobin.SP_CE)
          self.submodules.rr_read = roundrobin.RoundRobin(len(masters), roundrobin.SP_CE)
  
          def get_sig(interface, channel, name):
              return getattr(getattr(interface, channel), name)
  
          # Mux master->slave signals
          for channel, name, direction in target.layout_flat():
              rr = self.rr_write if channel in ["aw", "w", "b"] else self.rr_read
              if direction == DIR_M_TO_S:
                  choices = Array(get_sig(m, channel, name) for m in masters)
                  self.comb += get_sig(target, channel, name).eq(choices[rr.grant])
  
          # Connect slave->master signals
          for channel, name, direction in target.layout_flat():
              rr = self.rr_write if channel in ["aw", "w", "b"] else self.rr_read
              if direction == DIR_S_TO_M:
                  source = get_sig(target, channel, name)
                  for i, m in enumerate(masters):
                      dest = get_sig(m, channel, name)
                      if name == "ready":
                          self.comb += dest.eq(source & (rr.grant == i))
                      else:
                          self.comb += dest.eq(source)
  
          # Allow to change rr.grant only after all requests from a master have been responded to.
          self.submodules.wr_lock = wr_lock = _AXILiteRequestCounter(
              request=target.aw.valid & target.aw.ready, response=target.b.valid & target.b.ready)
          self.submodules.rd_lock = rd_lock = _AXILiteRequestCounter(
              request=target.ar.valid & target.ar.ready, response=target.r.valid & target.r.ready)
  
          # Switch to next request only if there are no responses pending.
          self.comb += [
              self.rr_write.ce.eq(~(target.aw.valid | target.w.valid | target.b.valid) & wr_lock.ready),
              self.rr_read.ce.eq(~(target.ar.valid | target.r.valid) & rd_lock.ready),
          ]
  
          # Connect bus requests to round-robin selectors.
          self.comb += [
              self.rr_write.request.eq(Cat(*[m.aw.valid | m.w.valid | m.b.valid for m in masters])),
              self.rr_read.request.eq(Cat(*[m.ar.valid | m.r.valid for m in masters])),
          ]
  
  class AXILiteDecoder(Module):
      """AXI Lite decoder
  
      Decode master access to particular slave based on its decoder function.
  
      slaves: [(decoder, slave), ...]
          List of slaves with address decoders, where `decoder` is a function:
              decoder(Signal(address_width - log2(data_width//8))) -> Signal(1)
          that returns 1 when the slave is selected and 0 otherwise.
      """
      def __init__(self, master, slaves, register=False):
          # TODO: unused register argument
          addr_shift = log2_int(master.data_width//8)
  
          channels = {
              "write": {"aw", "w", "b"},
              "read":  {"ar", "r"},
          }
          # Reverse mapping: directions[channel] -> "write"/"read".
          directions = {ch: d for d, chs in channels.items() for ch in chs}
  
          def new_slave_sel():
              return {"write": Signal(len(slaves)), "read":  Signal(len(slaves))}
  
          slave_sel_dec = new_slave_sel()
          slave_sel_reg = new_slave_sel()
          slave_sel     = new_slave_sel()
  
          # We need to hold the slave selected until all responses come back.
          # TODO: we could reuse arbiter counters
          locks = {
              "write": _AXILiteRequestCounter(
                  request=master.aw.valid & master.aw.ready,
                  response=master.b.valid & master.b.ready),
              "read": _AXILiteRequestCounter(
                  request=master.ar.valid & master.ar.ready,
                  response=master.r.valid & master.r.ready),
          }
          self.submodules += locks.values()
  
          def get_sig(interface, channel, name):
              return getattr(getattr(interface, channel), name)
  
          # # #
  
          # Decode slave addresses.
          for i, (decoder, bus) in enumerate(slaves):
              self.comb += [
                  slave_sel_dec["write"][i].eq(decoder(master.aw.addr[addr_shift:])),
                  slave_sel_dec["read"][i].eq(decoder(master.ar.addr[addr_shift:])),
              ]
  
          # Dhange the current selection only when we've got all responses.
          for channel in locks.keys():
              self.sync += If(locks[channel].ready, slave_sel_reg[channel].eq(slave_sel_dec[channel]))
          # We have to cut the delaying select.
          for ch, final in slave_sel.items():
              self.comb += If(locks[ch].ready,
                               final.eq(slave_sel_dec[ch])
                           ).Else(
                               final.eq(slave_sel_reg[ch])
                           )
  
          # Connect master->slaves signals except valid/ready.
          for i, (_, slave) in enumerate(slaves):
              for channel, name, direction in master.layout_flat():
                  if direction == DIR_M_TO_S:
                      src = get_sig(master, channel, name)
                      dst = get_sig(slave, channel, name)
                      # Mask master control signals depending on slave selection.
                      if name in ["valid", "ready"]:
                          src = src & slave_sel[directions[channel]][i]
                      self.comb += dst.eq(src)
  
          # Connect slave->master signals masking not selected slaves.
          for channel, name, direction in master.layout_flat():
              if direction == DIR_S_TO_M:
                  dst = get_sig(master, channel, name)
                  masked = []
                  for i, (_, slave) in enumerate(slaves):
                      src = get_sig(slave, channel, name)
                      # Mask depending on channel.
                      mask = Replicate(slave_sel[directions[channel]][i], len(dst))
                      masked.append(src & mask)
                  self.comb += dst.eq(reduce(or_, masked))
  
  class AXILiteInterconnectShared(Module):
      """AXI Lite shared interconnect"""
      def __init__(self, masters, slaves, register=False, timeout_cycles=1e6):
          # TODO: data width
          shared = AXILiteInterface()
          self.submodules.arbiter = AXILiteArbiter(masters, shared)
          self.submodules.decoder = AXILiteDecoder(shared, slaves)
          if timeout_cycles is not None:
              self.submodules.timeout = AXILiteTimeout(shared, timeout_cycles)
  
  class AXILiteCrossbar(Module):
      """AXI Lite crossbar
  
      MxN crossbar for M masters and N slaves.
      """
      def __init__(self, masters, slaves, register=False, timeout_cycles=1e6):
          matches, busses = zip(*slaves)
          access_m_s = [[AXILiteInterface() for j in slaves] for i in masters]  # a[master][slave]
          access_s_m = list(zip(*access_m_s))  # a[slave][master]
          # Decode each master into its access row.
          for slaves, master in zip(access_m_s, masters):
              slaves = list(zip(matches, slaves))
              self.submodules += AXILiteDecoder(master, slaves, register)
          # Arbitrate each access column onto its slave.
          for masters, bus in zip(access_s_m, busses):
              self.submodules += AXILiteArbiter(masters, bus)
  