Skip to content

Commit

Permalink
Merge pull request #1267 from antmicro/wb-sram-inc-burst
Browse files Browse the repository at this point in the history
soc/interconnect/wishbone: Add incrementing address burst mode to SRAM
  • Loading branch information
enjoy-digital authored Apr 15, 2022
2 parents 8b2fc20 + 8c1bc13 commit 9c4778b
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 9 deletions.
11 changes: 8 additions & 3 deletions litex/soc/integration/soc.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class SoCBusHandler(Module):
supported_address_width = [32]

# Creation -------------------------------------------------------------------------------------
def __init__(self, name="SoCBusHandler", standard="wishbone", data_width=32, address_width=32, timeout=1e6, reserved_regions={}):
def __init__(self, name="SoCBusHandler", standard="wishbone", data_width=32, address_width=32, timeout=1e6, bursting=False, reserved_regions={}):
self.logger = logging.getLogger(name)
self.logger.info("Creating Bus Handler...")

Expand Down Expand Up @@ -149,6 +149,7 @@ def __init__(self, name="SoCBusHandler", standard="wishbone", data_width=32, add
self.standard = standard
self.data_width = data_width
self.address_width = address_width
self.bursting = bursting
self.masters = {}
self.slaves = {}
self.regions = {}
Expand Down Expand Up @@ -719,6 +720,7 @@ def __init__(self, platform, sys_clk_freq,
bus_data_width = 32,
bus_address_width = 32,
bus_timeout = 1e6,
bus_bursting = False,
bus_reserved_regions = {},

csr_data_width = 32,
Expand Down Expand Up @@ -756,6 +758,7 @@ def __init__(self, platform, sys_clk_freq,
data_width = bus_data_width,
address_width = bus_address_width,
timeout = bus_timeout,
bursting = bus_bursting,
reserved_regions = bus_reserved_regions,
)

Expand Down Expand Up @@ -846,7 +849,7 @@ def add_ram(self, name, origin, size, contents=[], mode="rw"):
"wishbone": wishbone.Interface,
"axi-lite": axi.AXILiteInterface,
}[self.bus.standard]
ram_bus = interface_cls(data_width=self.bus.data_width)
ram_bus = interface_cls(data_width=self.bus.data_width, bursting=self.bus.bursting)
ram = ram_cls(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
self.check_if_exists(name)
Expand Down Expand Up @@ -991,6 +994,7 @@ def add_cpu(self, name="vexriscv", variant="standard", reset_address=None, cfu=N
standard = "wishbone",
data_width = self.bus.data_width,
address_width = self.bus.address_width,
bursting = self.bus.bursting
)
dma_bus = wishbone.Interface(data_width=self.bus.data_width)
self.dma_bus.add_slave("dma", slave=dma_bus, region=SoCRegion(origin=0x00000000, size=0x100000000)) # FIXME: covers lower 4GB only
Expand Down Expand Up @@ -1078,6 +1082,7 @@ def do_finalize(self):
self.add_constant("CONFIG_BUS_STANDARD", self.bus.standard.upper())
self.add_constant("CONFIG_BUS_DATA_WIDTH", self.bus.data_width)
self.add_constant("CONFIG_BUS_ADDRESS_WIDTH", self.bus.address_width)
self.add_constant("CONFIG_BUS_BURSTING", int(self.bus.bursting))

# SoC DMA Bus Interconnect (Cache Coherence) -----------------------------------------------
if hasattr(self, "dma_bus"):
Expand Down Expand Up @@ -1971,4 +1976,4 @@ def get_selected_cpu_name():
if cpu_cls is not None and hasattr(cpu_cls, "args_read"):
cpu_cls.args_read(args)

return args
return args
3 changes: 3 additions & 0 deletions litex/soc/integration/soc_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def __init__(self, platform, clk_freq,
bus_data_width = 32,
bus_address_width = 32,
bus_timeout = 1e6,
bus_bursting = False,

# CPU parameters
cpu_type = "vexriscv",
Expand Down Expand Up @@ -122,6 +123,7 @@ def __init__(self, platform, clk_freq,
bus_data_width = bus_data_width,
bus_address_width = bus_address_width,
bus_timeout = bus_timeout,
bus_bursting = bus_bursting,
bus_reserved_regions = {},

csr_data_width = csr_data_width,
Expand Down Expand Up @@ -301,6 +303,7 @@ def soc_core_args(parser):
soc_group.add_argument("--bus-data-width", default=32, type=auto_int, help="Bus data-width.")
soc_group.add_argument("--bus-address-width", default=32, type=auto_int, help="Bus address-width.")
soc_group.add_argument("--bus-timeout", default=int(1e6), type=float, help="Bus timeout in cycles.")
soc_group.add_argument("--bus-bursting", action="store_true", help="Enable burst cycles on the bus if supported.")

# CPU parameters
soc_group.add_argument("--cpu-type", default="vexriscv", help="Select CPU: {}.".format(", ".join(iter(cpu.CPUS.keys()))))
Expand Down
4 changes: 3 additions & 1 deletion litex/soc/interconnect/axi.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,12 @@ def r_lite_description(data_width):
]

class AXILiteInterface:
def __init__(self, data_width=32, address_width=32, clock_domain="sys", name=None):
def __init__(self, data_width=32, address_width=32, clock_domain="sys", name=None, bursting=False):
self.data_width = data_width
self.address_width = address_width
self.clock_domain = clock_domain
if bursting is not False:
raise NotImplementedError("AXI-Lite does not support bursting")

self.aw = stream.Endpoint(ax_lite_description(address_width), name=name)
self.w = stream.Endpoint(w_lite_description(data_width), name=name)
Expand Down
104 changes: 99 additions & 5 deletions litex/soc/interconnect/wishbone.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Copyright (c) 2015 Sebastien Bourdeauducq <[email protected]>
# Copyright (c) 2015-2020 Florent Kermarrec <[email protected]>
# Copyright (c) 2018 Tim 'mithro' Ansell <[email protected]>
# Copytight (c) 2022 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: BSD-2-Clause

"""Wishbone Classic support for LiteX (Standard HandShaking/Synchronous Feedback)"""
Expand Down Expand Up @@ -38,11 +39,17 @@
("err", 1, DIR_S_TO_M)
]

CTI_BURST_NONE = 0b000
CTI_BURST_CONSTANT = 0b001
CTI_BURST_INCREMENTING = 0b010
CTI_BURST_END = 0b111


class Interface(Record):
def __init__(self, data_width=32, adr_width=30):
def __init__(self, data_width=32, adr_width=30, bursting=False):
self.data_width = data_width
self.adr_width = adr_width
self.bursting = bursting
Record.__init__(self, set_layout_parameters(_layout,
adr_width = adr_width,
data_width = data_width,
Expand All @@ -65,18 +72,26 @@ def _do_transaction(self):
yield self.cyc.eq(0)
yield self.stb.eq(0)

def write(self, adr, dat, sel=None):
def write(self, adr, dat, sel=None, cti=None, bte=None):
if sel is None:
sel = 2**len(self.sel) - 1
yield self.adr.eq(adr)
yield self.dat_w.eq(dat)
yield self.sel.eq(sel)
if cti is not None:
yield self.cti.eq(cti)
if bte is not None:
yield self.bte.eq(bte)
yield self.we.eq(1)
yield from self._do_transaction()

def read(self, adr):
def read(self, adr, cti=None, bte=None):
yield self.adr.eq(adr)
yield self.we.eq(0)
if cti is not None:
yield self.cti.eq(cti)
if bte is not None:
yield self.bte.eq(bte)
yield from self._do_transaction()
return (yield self.dat_r)

Expand Down Expand Up @@ -348,6 +363,80 @@ def __init__(self, mem_or_size, read_only=None, init=None, bus=None):

###

adr_burst = Signal()

if self.bus.bursting:
adr_wrap_mask = Array((0b0000, 0b0011, 0b0111, 0b1111))
adr_wrap_max = adr_wrap_mask[-1].bit_length()

adr_burst_wrap = Signal()
adr_latched = Signal()

adr_counter = Signal(len(self.bus.adr))
adr_counter_base = Signal(len(self.bus.adr))
adr_counter_offset = Signal(adr_wrap_max)
adr_offset_lsb = Signal(adr_wrap_max)
adr_offset_msb = Signal(len(self.bus.adr))

adr_next = Signal(len(self.bus.adr))

# only incrementing burst cycles are supported
self.comb += [
Case(self.bus.cti, {
# incrementing address burst cycle
CTI_BURST_INCREMENTING: adr_burst.eq(1),
# end current burst cycle
CTI_BURST_END: adr_burst.eq(0),
# unsupported burst cycle
"default": adr_burst.eq(0)
}),
adr_burst_wrap.eq(self.bus.bte[0] | self.bus.bte[1]),
adr_counter_base.eq(
Cat(self.bus.adr & ~adr_wrap_mask[self.bus.bte],
self.bus.adr[adr_wrap_max:]
)
)
]

# latch initial address - initial address without wrapping bits and wrap offset
self.sync += [
If(self.bus.cyc & self.bus.stb & adr_burst,
adr_latched.eq(1),
# latch initial address, then increment it every clock cycle
If(adr_latched,
adr_counter.eq(adr_counter + 1)
).Else(
adr_counter_offset.eq(self.bus.adr & adr_wrap_mask[self.bus.bte]),
adr_counter.eq(adr_counter_base + Cat(~self.bus.we,
Replicate(0, len(adr_counter)-1)
)
)
),
If(self.bus.cti == CTI_BURST_END,
adr_latched.eq(0),
adr_counter.eq(0),
adr_counter_offset.eq(0)
)
).Else(
adr_latched.eq(0),
adr_counter.eq(0),
adr_counter_offset.eq(0)
),
]

# next address = sum of counter value without wrapped bits
# and wrapped counter bits with offset
self.comb += [
adr_offset_lsb.eq((adr_counter + adr_counter_offset) & adr_wrap_mask[self.bus.bte]),
adr_offset_msb.eq(adr_counter & ~adr_wrap_mask[self.bus.bte]),
adr_next.eq(adr_offset_msb + adr_offset_lsb)
]

else: # self.ram.bursting == False
self.comb += adr_burst.eq(0)

###

# memory
port = self.mem.get_port(write_capable=not read_only, we_granularity=8,
mode=READ_FIRST if read_only else WRITE_FIRST)
Expand All @@ -357,16 +446,21 @@ def __init__(self, mem_or_size, read_only=None, init=None, bus=None):
self.comb += [port.we[i].eq(self.bus.cyc & self.bus.stb & self.bus.we & self.bus.sel[i])
for i in range(bus_data_width//8)]
# address and data
self.comb += port.adr.eq(self.bus.adr[:len(port.adr)])
if self.bus.bursting:
self.comb += If(adr_burst & adr_latched,
port.adr.eq(adr_next[:len(port.adr)]),
)
self.comb += [
port.adr.eq(self.bus.adr[:len(port.adr)]),
self.bus.dat_r.eq(port.dat_r)
]
if not read_only:
self.comb += port.dat_w.eq(self.bus.dat_w),

# generate ack
self.sync += [
self.bus.ack.eq(0),
If(self.bus.cyc & self.bus.stb & ~self.bus.ack, self.bus.ack.eq(1))
If(self.bus.cyc & self.bus.stb & (~self.bus.ack | adr_burst), self.bus.ack.eq(1))
]

# Wishbone To CSR ----------------------------------------------------------------------------------
Expand Down
61 changes: 61 additions & 0 deletions test/test_wishbone.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,64 @@ def __init__(self):

dut = DUT()
run_simulation(dut, generator(dut))

def test_sram_burst(self):
def generator(dut):
yield from dut.wb.write(0x0000, 0x01234567, cti=wishbone.CTI_BURST_INCREMENTING)
yield from dut.wb.write(0x0001, 0x89abcdef, cti=wishbone.CTI_BURST_INCREMENTING)
yield from dut.wb.write(0x0002, 0xdeadbeef, cti=wishbone.CTI_BURST_INCREMENTING)
yield from dut.wb.write(0x0003, 0xc0ffee00, cti=wishbone.CTI_BURST_END)
self.assertEqual((yield from dut.wb.read(0x0000, cti=wishbone.CTI_BURST_INCREMENTING)), 0x01234567)
self.assertEqual((yield from dut.wb.read(0x0001, cti=wishbone.CTI_BURST_INCREMENTING)), 0x89abcdef)
self.assertEqual((yield from dut.wb.read(0x0002, cti=wishbone.CTI_BURST_INCREMENTING)), 0xdeadbeef)
self.assertEqual((yield from dut.wb.read(0x0003, cti=wishbone.CTI_BURST_END)), 0xc0ffee00)

class DUT(Module):
def __init__(self):
self.wb = wishbone.Interface(bursting=True)
wishbone_mem = wishbone.SRAM(32, bus=self.wb)
self.submodules += wishbone_mem

dut = DUT()
run_simulation(dut, generator(dut))

def test_sram_burst_wrap(self):
def generator(dut):
bte = 0b01
yield from dut.wb.write(0x0001, 0x01234567, cti=wishbone.CTI_BURST_INCREMENTING, bte=bte)
yield from dut.wb.write(0x0002, 0x89abcdef, cti=wishbone.CTI_BURST_INCREMENTING, bte=bte)
yield from dut.wb.write(0x0003, 0xdeadbeef, cti=wishbone.CTI_BURST_INCREMENTING, bte=bte)
yield from dut.wb.write(0x0000, 0xc0ffee00, cti=wishbone.CTI_BURST_END, bte=bte)
self.assertEqual((yield from dut.wb.read(0x0001, cti=wishbone.CTI_BURST_INCREMENTING, bte=bte)), 0x01234567)
self.assertEqual((yield from dut.wb.read(0x0002, cti=wishbone.CTI_BURST_INCREMENTING, bte=bte)), 0x89abcdef)
self.assertEqual((yield from dut.wb.read(0x0003, cti=wishbone.CTI_BURST_INCREMENTING, bte=bte)), 0xdeadbeef)
self.assertEqual((yield from dut.wb.read(0x0000, cti=wishbone.CTI_BURST_END, bte=bte)), 0xc0ffee00)

class DUT(Module):
def __init__(self):
self.wb = wishbone.Interface(bursting=True)
wishbone_mem = wishbone.SRAM(32, bus=self.wb)
self.submodules += wishbone_mem

dut = DUT()
run_simulation(dut, generator(dut))

def test_sram_burst_constant(self):
def generator(dut):
yield from dut.wb.write(0x0001, 0x01234567, cti=wishbone.CTI_BURST_CONSTANT)
yield from dut.wb.write(0x0002, 0x89abcdef, cti=wishbone.CTI_BURST_CONSTANT)
yield from dut.wb.write(0x0003, 0xdeadbeef, cti=wishbone.CTI_BURST_CONSTANT)
yield from dut.wb.write(0x0000, 0xc0ffee00, cti=wishbone.CTI_BURST_END)
self.assertEqual((yield from dut.wb.read(0x0001, cti=wishbone.CTI_BURST_CONSTANT)), 0x01234567)
self.assertEqual((yield from dut.wb.read(0x0002, cti=wishbone.CTI_BURST_CONSTANT)), 0x89abcdef)
self.assertEqual((yield from dut.wb.read(0x0003, cti=wishbone.CTI_BURST_CONSTANT)), 0xdeadbeef)
self.assertEqual((yield from dut.wb.read(0x0000, cti=wishbone.CTI_BURST_END)), 0xc0ffee00)

class DUT(Module):
def __init__(self):
self.wb = wishbone.Interface(bursting=True)
wishbone_mem = wishbone.SRAM(32, bus=self.wb)
self.submodules += wishbone_mem

dut = DUT()
run_simulation(dut, generator(dut))

0 comments on commit 9c4778b

Please sign in to comment.