class Access(enum.Enum):
"""Register access mode.
- Coarse access mode for the entire register. Individual fields can have more restrictive
- access mode, e.g. R/O fields can be a part of an R/W register.
+ Coarse access mode for the entire register. Individual fields
+ can have more restrictive access mode, e.g. R/O fields can be
+ a part of an R/W register.
"""
R = "r"
W = "w"
"""Peripheral-side CSR interface.
- A low-level interface to a single atomically readable and writable register in a peripheral.
- This interface supports any register width and semantics, provided that both reads and writes
- always succeed and complete in one cycle.
+ A low-level interface to a single atomically readable and writable
+ register in a peripheral. This interface supports any register
+ width and semantics, provided that both reads and writes always
+ succeed and complete in one cycle.
Parameters
----------
Attributes
----------
r_data : Signal(width)
- Read data. Must be always valid, and is sampled when ``r_stb`` is asserted.
+ Read data. Must be always valid, and is sampled when ``r_stb``
+ is asserted.
r_stb : Signal()
- Read strobe. Registers with read side effects should perform the read side effect when this
- strobe is asserted.
+ Read strobe. Registers with read side effects should perform
+ the read side effect when this strobe is asserted.
w_data : Signal(width)
Write data. Valid only when ``w_stb`` is asserted.
w_stb : Signal()
- Write strobe. Registers should update their value or perform the write side effect when
- this strobe is asserted.
+ Write strobe. Registers should update their value or perform
+ the write side effect when this strobe is asserted.
"""
def __init__(self, width, access, *, name=None, src_loc_at=0):
if not isinstance(width, int) or width < 0:
class Interface(Record):
"""CPU-side CSR interface.
- A low-level interface to a set of atomically readable and writable peripheral CSR registers.
+ A low-level interface to a set of atomically readable and writable
+ peripheral CSR registers.
Operation
---------
- CSR registers mapped to the CSR bus are split into chunks according to the bus data width.
- Each chunk is assigned a consecutive address on the bus. This allows accessing CSRs of any
- size using any datapath width.
+ CSR registers mapped to the CSR bus are split into chunks according to
+ the bus data width. Each chunk is assigned a consecutive address on
+ the bus. This allows accessing CSRs of any size using any datapath
+ width.
- When the first chunk of a register is read, the value of a register is captured, and reads
- from subsequent chunks of the same register return the captured values. When any chunk except
- the last chunk of a register is written, the written value is captured; a write to the last
- chunk writes the captured value to the register. This allows atomically accessing CSRs larger
- than datapath width.
+ When the first chunk of a register is read, the value of a register
+ is captured, and reads from subsequent chunks of the same register
+ return the captured values. When any chunk except the last chunk
+ of a register is written, the written value is captured; a write
+ to the last chunk writes the captured value to the register. This
+ allows atomically accessing CSRs larger than datapath width.
Parameters
----------
addr_width : int
- Address width. At most ``(2 ** addr_width) * data_width`` register bits will be available.
+ Address width. At most ``(2 ** addr_width) * data_width``
+ register bits will be available.
data_width : int
Data width. Registers are accessed in ``data_width`` sized chunks.
alignment : int
addr : Signal(addr_width)
Address for reads and writes.
r_data : Signal(data_width)
- Read data. Valid on the next cycle after ``r_stb`` is asserted. Otherwise, zero. (Keeping
- read data of an unused interface at zero simplifies multiplexers.)
+ Read data. Valid on the next cycle after ``r_stb`` is
+ asserted. Otherwise, zero. (Keeping read data of an unused
+ interface at zero simplifies multiplexers.)
r_stb : Signal()
- Read strobe. If ``addr`` points to the first chunk of a register, captures register value
- and causes read side effects to be performed (if any). If ``addr`` points to any chunk
- of a register, latches the captured value to ``r_data``. Otherwise, latches zero
- to ``r_data``.
+ Read strobe. If ``addr`` points to the first chunk of a
+ register, captures register value and causes read side effects
+ to be performed (if any). If ``addr`` points to any chunk of a
+ register, latches the captured value to ``r_data``. Otherwise,
+ latches zero to ``r_data``.
w_data : Signal(data_width)
Write data. Must be valid when ``w_stb`` is asserted.
w_stb : Signal()
- Write strobe. If ``addr`` points to the last chunk of a register, writes captured value
- to the register and causes write side effects to be performed (if any). If ``addr`` points
- to any chunk of a register, latches ``w_data`` to the captured value. Otherwise, does
- nothing.
+ Write strobe. If ``addr`` points to the last chunk of a register,
+ writes captured value to the register and causes write side
+ effects to be performed (if any). If ``addr`` points to
+ any chunk of a register, latches ``w_data`` to the captured
+ value. Otherwise, does nothing.
"""
def __init__(self, *, addr_width, data_width, alignment=0, name=None):
Latency
-------
- Writes are registered, and are performed 1 cycle after ``w_stb`` is asserted.
+ Writes are registered, and are performed 1 cycle after ``w_stb``
+ is asserted.
Alignment
---------
- Because the CSR bus conserves logic and routing resources, it is common to e.g. access
- a CSR bus with an *n*-bit data path from a CPU with a *k*-bit datapath (*k>n*) in cases
- where CSR access latency is less important than resource usage. In this case, two strategies
+ Because the CSR bus conserves logic and routing resources, it is
+ common to e.g. access a CSR bus with an *n*-bit data path from a CPU
+ with a *k*-bit datapath (*k>n*) in cases where CSR access latency
+ is less important than resource usage. In this case, two strategies
are possible for connecting the CSR bus to the CPU:
- * The CPU could access the CSR bus directly (with no intervening logic other than simple
- translation of control signals). In this case, the register alignment should be set
- to 1, and each *w*-bit register would occupy *ceil(w/n)* addresses from the CPU
- perspective, requiring the same amount of memory instructions to access.
- * The CPU could also access the CSR bus through a width down-converter, which would issue
- *k/n* CSR accesses for each CPU access. In this case, the register alignment should be
- set to *k/n*, and each *w*-bit register would occupy *ceil(w/k)* addresses from the CPU
- perspective, requiring the same amount of memory instructions to access.
-
- If alignment is greater than 1, it affects which CSR bus write is considered a write to
- the last register chunk. For example, if a 24-bit register is used with a 8-bit CSR bus and
- a CPU with a 32-bit datapath, a write to this register requires 4 CSR bus writes to complete
- and the 4th write is the one that actually writes the value to the register. This allows
- determining write latency solely from the amount of addresses the register occupies in
- the CPU address space, and the width of the CSR bus.
+ * The CPU could access the CSR bus directly (with no intervening
+ logic other than simple translation of control signals). In
+ this case, the register alignment should be set to 1, and each
+ *w*-bit register would occupy *ceil(w/n)* addresses from the CPU
+ perspective, requiring the same amount of memory instructions
+ to access.
+ * The CPU could also access the CSR bus through a width
+ down-converter, which would issue *k/n* CSR accesses for each
+ CPU access. In this case, the register alignment should be set
+ to *k/n*, and each *w*-bit register would occupy *ceil(w/k)*
+ addresses from the CPU perspective, requiring the same amount
+ of memory instructions to access.
+
+ If alignment is greater than 1, it affects which CSR bus write
+ is considered a write to the last register chunk. For example,
+ if a 24-bit register is used with a 8-bit CSR bus and a CPU with a
+ 32-bit datapath, a write to this register requires 4 CSR bus writes
+ to complete and the 4th write is the one that actually writes the
+ value to the register. This allows determining write latency solely
+ from the amount of addresses the register occupies in the CPU address
+ space, and the width of the CSR bus.
Parameters
----------
Usage
-----
- Although there is no functional difference between adding a set of registers directly to
- a :class:`Multiplexer` and adding a set of registers to multiple :class:`Multiplexer`s that are
- aggregated with a :class:`Decoder`, hierarchical CSR buses are useful for organizing
- a hierarchical design. If many peripherals are directly served by a single
- :class:`Multiplexer`, a very large amount of ports will connect the peripheral registers with
- the decoder, and the cost of decoding logic would not be attributed to specific peripherals.
- With a decoder, only five signals per peripheral will be used, and the logic could be kept
- together with the peripheral.
+ Although there is no functional difference between adding a set of
+ registers directly to a :class:`Multiplexer` and adding a set of
+ registers to multiple :class:`Multiplexer`s that are aggregated with
+ a :class:`Decoder`, hierarchical CSR buses are useful for organizing
+ a hierarchical design. If many peripherals are directly served by
+ a single :class:`Multiplexer`, a very large amount of ports will
+ connect the peripheral registers with the decoder, and the cost of
+ decoding logic would not be attributed to specific peripherals.
+ With a decoder, only five signals per peripheral will be used,
+ and the logic could be kept together with the peripheral.
Parameters
----------
class WishboneCSRBridge(Elaboratable):
"""Wishbone to CSR bridge.
- A bus bridge for accessing CSR registers from Wishbone. This bridge supports any Wishbone
- data width greater or equal to CSR data width and performs appropriate address translation.
+ A bus bridge for accessing CSR registers from Wishbone. This bridge
+ supports any Wishbone data width greater or equal to CSR data width
+ and performs appropriate address translation.
Latency
-------
- Reads and writes always take ``self.data_width // csr_bus.data_width + 1`` cycles to complete,
- regardless of the select inputs. Write side effects occur simultaneously with acknowledgement.
+ Reads and writes always take ``self.data_width // csr_bus.data_width +
+ 1`` cycles to complete, regardless of the select inputs. Write side
+ effects occur simultaneously with acknowledgement.
Parameters
----------
csr_bus : :class:`..csr.Interface`
CSR bus driven by the bridge.
data_width : int or None
- Wishbone bus data width. If not specified, defaults to ``csr_bus.data_width``.
+ Wishbone bus data width. If not specified, defaults to
+ ``csr_bus.data_width``.
Attributes
----------
class MemoryMap:
"""Memory map.
- A memory map is a hierarchical description of an address space, describing the structure of
- address decoders of peripherals as well as bus bridges. It is built by adding resources
- (range allocations for registers, memory, etc) and windows (range allocations for bus bridges),
- and can be queried later to determine the address of any given resource from a specific
- vantage point in the design.
+ A memory map is a hierarchical description of an address space,
+ describing the structure of address decoders of peripherals as well
+ as bus bridges. It is built by adding resources (range allocations
+ for registers, memory, etc) and windows (range allocations for bus
+ bridges), and can be queried later to determine the address of any
+ given resource from a specific vantage point in the design.
Address assignment
------------------
- To simplify address assignment, each memory map has an implicit next address, starting at 0.
- If a resource or a window is added without specifying an address explicitly, the implicit next
- address is used. In any case, the implicit next address is set to the address immediately
- following the newly added resource or window.
+ To simplify address assignment, each memory map has an implicit
+ next address, starting at 0. If a resource or a window is added
+ without specifying an address explicitly, the implicit next address
+ is used. In any case, the implicit next address is set to the address
+ immediately following the newly added resource or window.
Parameters
----------
data_width : int
Data width.
alignment : int
- Range alignment. Each added resource and window will be placed at an address that is
- a multiple of ``2 ** alignment``, and its size will be rounded up to be a multiple of
- ``2 ** alignment``.
+ Range alignment. Each added resource and window will be placed
+ at an address that is a multiple of ``2 ** alignment``, and its
+ size will be rounded up to be a multiple of ``2 ** alignment``.
"""
def __init__(self, *, addr_width, data_width, alignment=0):
if not isinstance(addr_width, int) or addr_width <= 0:
Arguments
---------
alignment : int
- Address alignment. The start of the implicit next address will be a multiple of
- ``2 ** max(alignment, self.alignment)``.
+ Address alignment. The start of the implicit next address
+ will be a multiple of ``2 ** max(alignment, self.alignment)``.
Return value
------------
def add_resource(self, resource, *, size, addr=None, alignment=None):
"""Add a resource.
- A resource is any device on the bus that is a destination for bus transactions, e.g.
- a register or a memory block.
+ A resource is any device on the bus that is a destination for
+ bus transactions, e.g. a register or a memory block.
Arguments
---------
resource : object
Arbitrary object representing a resource.
addr : int or None
- Address of the resource. If ``None``, the implicit next address will be used.
- Otherwise, the exact specified address (which must be a multiple of
+ Address of the resource. If ``None``, the implicit next
+ address will be used. Otherwise, the exact specified address
+ (which must be a multiple of
``2 ** max(alignment, self.alignment)``) will be used.
size : int
- Size of the resource, in minimal addressable units. Rounded up to a multiple of
- ``2 ** max(alignment, self.alignment)``.
+ Size of the resource, in minimal addressable units. Rounded
+ up to a multiple of ``2 ** max(alignment, self.alignment)``.
alignment : int or None
- Alignment of the resource. If not specified, the memory map alignment is used.
+ Alignment of the resource. If not specified, the memory map
+ alignment is used.
Return value
------------
- A tuple ``(start, end)`` describing the address range assigned to the resource.
+ A tuple ``(start, end)`` describing the address range assigned
+ to the resource.
Exceptions
----------
- Raises :exn:`ValueError` if the requested address and size, after alignment, would overlap
- with any resources or windows that have already been added, or would be out of bounds.
+ Raises :exn:`ValueError` if the requested address and size,
+ after alignment, would overlap with any resources or windows
+ that have already been added, or would be out of bounds.
"""
if resource in self._resources:
addr_range = self._resources[resource]
Yield values
------------
- A tuple ``resource, (start, end)`` describing the address range assigned to the resource.
+ A tuple ``resource, (start, end)`` describing the address range
+ assigned to the resource.
"""
for resource, resource_range in self._resources.items():
yield resource, (resource_range.start, resource_range.stop)
def add_window(self, window, *, addr=None, sparse=None):
"""Add a window.
- A window is a device on a bus that provides access to a different bus, i.e. a bus bridge.
- It performs address translation, such that the devices on a subordinate bus have different
- addresses; the memory map reflects this address translation when resources are looked up
- through the window.
+ A window is a device on a bus that provides access to a different
+ bus, i.e. a bus bridge. It performs address translation, such
+ that the devices on a subordinate bus have different addresses;
+ the memory map reflects this address translation when resources
+ are looked up through the window.
Sparse addressing
-----------------
- If a narrow bus is bridged to a wide bus, the bridge can perform *sparse* or *dense*
- address translation. In the sparse case, each transaction on the wide bus results in
- one transaction on the narrow bus; high data bits on the wide bus are ignored, and any
- contiguous resource on the narrow bus becomes discontiguous on the wide bus. In the dense
- case, each transaction on the wide bus results in several transactions on the narrow bus,
- and any contiguous resource on the narrow bus stays contiguous on the wide bus.
+ If a narrow bus is bridged to a wide bus, the bridge can perform
+ *sparse* or *dense* address translation. In the sparse case,
+ each transaction on the wide bus results in one transaction on
+ the narrow bus; high data bits on the wide bus are ignored, and
+ any contiguous resource on the narrow bus becomes discontiguous
+ on the wide bus. In the dense case, each transaction on the
+ wide bus results in several transactions on the narrow bus,
+ and any contiguous resource on the narrow bus stays contiguous
+ on the wide bus.
Arguments
---------
window : :class:`MemoryMap`
A memory map describing the layout of the window.
addr : int or None
- Address of the window. If ``None``, the implicit next address will be used after
- aligning it to ``2 ** window.addr_width``. Otherwise, the exact specified address
- (which must be a multiple of ``2 ** window.addr_width``) will be used.
+ Address of the window. If ``None``, the implicit
+ next address will be used after aligning it to ``2 **
+ window.addr_width``. Otherwise, the exact specified address
+ (which must be a multiple of ``2 ** window.addr_width``)
+ will be used.
sparse : bool or None
- Address translation type. Ignored if the datapath widths of both memory maps are
- equal; must be specified otherwise.
+ Address translation type. Ignored if the datapath widths of
+ both memory maps are equal; must be specified otherwise.
Return value
------------
- A tuple ``(start, end, ratio)`` describing the address range assigned to the window.
- When bridging buses of unequal data width, ``ratio`` is the amount of contiguous addresses
- on the narrower bus that are accessed for each transaction on the wider bus. Otherwise,
- it is always 1.
+ A tuple ``(start, end, ratio)`` describing the address range
+ assigned to the window. When bridging buses of unequal data
+ width, ``ratio`` is the amount of contiguous addresses on the
+ narrower bus that are accessed for each transaction on the wider
+ bus. Otherwise, it is always 1.
Exceptions
----------
- Raises :exn:`ValueError` if the requested address and size, after alignment, would overlap
- with any resources or windows that have already been added, or would be out of bounds;
- if the added memory map has wider datapath than this memory map; if dense address
- translation is used and the datapath width of this memory map is not an integer multiple
- of the datapath width of the added memory map.
+ Raises :exn:`ValueError` if the requested address and size,
+ after alignment, would overlap with any resources or windows
+ that have already been added, or would be out of bounds; if
+ the added memory map has wider datapath than this memory map;
+ if dense address translation is used and the datapath width of
+ this memory map is not an integer multiple of the datapath width
+ of the added memory map.
"""
if not isinstance(window, MemoryMap):
raise TypeError("Window must be a MemoryMap, not {!r}"
Yield values
------------
- A tuple ``window, (start, end, ratio)`` describing the address range assigned to
- the window. When bridging buses of unequal data width, ``ratio`` is the amount of
- contiguous addresses on the narrower bus that are accessed for each transaction on
- the wider bus. Otherwise, it is always 1.
+ A tuple ``window, (start, end, ratio)`` describing the address
+ range assigned to the window. When bridging buses of unequal
+ data width, ``ratio`` is the amount of contiguous addresses on
+ the narrower bus that are accessed for each transaction on the
+ wider bus. Otherwise, it is always 1.
"""
for window, window_range in self._windows.items():
yield window, (window_range.start, window_range.stop, window_range.step)
Yield values
------------
- A tuple ``window, (pattern, ratio)`` describing the address range assigned to the window.
- ``pattern`` is a ``self.addr_width`` wide pattern that may be used in ``Case`` or ``match``
- to determine if an address signal is within the address range of ``window``. When bridging
- buses of unequal data width, ``ratio`` is the amount of contiguous addresses on
- the narrower bus that are accessed for each transaction on the wider bus. Otherwise,
+ A tuple ``window, (pattern, ratio)`` describing the address range
+ assigned to the window. ``pattern`` is a ``self.addr_width``
+ wide pattern that may be used in ``Case`` or ``match`` to
+ determine if an address signal is within the address range of
+ ``window``. When bridging buses of unequal data width, ``ratio``
+ is the amount of contiguous addresses on the narrower bus that
+ are accessed for each transaction on the wider bus. Otherwise,
it is always 1.
"""
for window, window_range in self._windows.items():
def all_resources(self):
"""Iterate all resources and their address ranges.
- Recursively iterate all resources in ascending order of their address, performing address
- translation for resources that are located behind a window.
+ Recursively iterate all resources in ascending order of their
+ address, performing address translation for resources that are
+ located behind a window.
- Yield values
- ------------
- A tuple ``resource, (start, end, width)`` describing the address range assigned to
- the resource. ``width`` is the amount of data bits accessed at each address, which may be
- equal to ``self.data_width``, or less if the resource is located behind a window that
- uses sparse addressing.
+ Yield values ------------ A tuple ``resource, (start,
+ end, width)`` describing the address range assigned to the
+ resource. ``width`` is the amount of data bits accessed at each
+ address, which may be equal to ``self.data_width``, or less if the
+ resource is located behind a window that uses sparse addressing.
"""
for addr_range, assignment in self._ranges.items():
if assignment in self._resources:
def find_resource(self, resource):
"""Find address range corresponding to a resource.
- Recursively find the address range of a resource, performing address translation for
- resources that are located behind a window.
+ Recursively find the address range of a resource, performing
+ address translation for resources that are located behind
+ a window.
Arguments
---------
Return value
------------
- A tuple ``(start, end, width)`` describing the address range assigned to the resource.
- ``width`` is the amount of data bits accessed at each address, which may be equal to
- ``self.data_width``, or less if the resource is located behind a window that uses sparse
- addressing.
+ A tuple ``(start, end, width)`` describing the address
+ range assigned to the resource. ``width`` is the amount of
+ data bits accessed at each address, which may be equal to
+ ``self.data_width``, or less if the resource is located behind
+ a window that uses sparse addressing.
Exceptions
----------
Return value
------------
- A resource mapped to the provided address, or ``None`` if there is no such resource.
+ A resource mapped to the provided address, or ``None`` if there
+ is no such resource.
"""
assignment = self._ranges.get(address)
if assignment is None:
Attributes
----------
request : Signal(n)
- Signal where a '1' on the i-th bit represents an incoming request from the i-th device.
+ Signal where a '1' on the i-th bit represents an incoming request
+ from the i-th device.
grant : Signal(range(n))
- Signal that equals to the index of the device which is currently granted access.
+ Signal that equals to the index of the device which is currently
+ granted access.
stb : Signal()
- Strobe signal to enable granting access to the next device requesting. Externally driven.
+ Strobe signal to enable granting access to the next device
+ requesting. Externally driven.
"""
def __init__(self, n):
self.n = n
with m.If(self.request[t]):
m.d.sync += self.grant.eq(t)
- return m
\ No newline at end of file
+ return m
def test_add_wrong_sub_bus(self):
with self.assertRaisesRegex(TypeError,
- r"Subordinate bus must be an instance of csr\.Interface, not 1"):
+ r"Subordinate bus must be an instance of csr\.Interface, not 1"):
self.dut.add(1)
def test_add_wrong_data_width(self):
def test_wrong_csr_bus_data_width(self):
with self.assertRaisesRegex(ValueError,
r"CSR bus data width must be one of 8, 16, 32, 64, not 7"):
- WishboneCSRBridge(csr_bus=csr.Interface(addr_width=10, data_width=7))
+ WishboneCSRBridge(csr_bus=csr.Interface(addr_width=10,
+ data_width=7))
def test_narrow(self):
mux = csr.Multiplexer(addr_width=10, data_width=8)
def test_add_resource_wrong_address_unaligned(self):
memory_map = MemoryMap(addr_width=16, data_width=8, alignment=1)
with self.assertRaisesRegex(ValueError,
- r"Explicitly specified address 0x1 must be a multiple of 0x2 bytes"):
+ r"Explicitly specified address 0x1 must be "
+ r"a multiple of 0x2 bytes"):
memory_map.add_resource("a", size=1, addr=1)
def test_add_resource_wrong_size(self):
def test_add_resource_wrong_out_of_bounds(self):
memory_map = MemoryMap(addr_width=16, data_width=8)
with self.assertRaisesRegex(ValueError,
- r"Address range 0x10000\.\.0x10001 out of bounds for memory map spanning "
- r"range 0x0\.\.0x10000 \(16 address bits\)"):
+ r"Address range 0x10000\.\.0x10001 out of bounds for memory "
+ r"map spanning range 0x0\.\.0x10000 \(16 address bits\)"):
memory_map.add_resource("a", addr=0x10000, size=1)
with self.assertRaisesRegex(ValueError,
- r"Address range 0x0\.\.0x10001 out of bounds for memory map spanning "
- r"range 0x0\.\.0x10000 \(16 address bits\)"):
+ r"Address range 0x0\.\.0x10001 out of bounds for memory map "
+ r"spanning range 0x0\.\.0x10000 \(16 address bits\)"):
memory_map.add_resource("a", size=0x10001)
def test_add_resource_wrong_overlap(self):
memory_map = MemoryMap(addr_width=16, data_width=8)
memory_map.add_resource("a", size=16)
with self.assertRaisesRegex(ValueError,
- r"Address range 0xa\.\.0xb overlaps with resource 'a' at 0x0\.\.0x10"):
+ r"Address range 0xa\.\.0xb overlaps with resource "
+ r"'a' at 0x0\.\.0x10"):
memory_map.add_resource("b", size=1, addr=10)
def test_add_resource_wrong_twice(self):
def test_add_window(self):
memory_map = MemoryMap(addr_width=16, data_width=8)
self.assertEqual(memory_map.add_resource("a", size=1), (0, 1))
- self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10, data_width=8)),
+ self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10,
+ data_width=8)),
(0x400, 0x800, 1))
self.assertEqual(memory_map.add_resource("b", size=1), (0x800, 0x801))
def test_add_window_sparse(self):
memory_map = MemoryMap(addr_width=16, data_width=32)
- self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10, data_width=8),
+ self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10,
+ data_width=8),
sparse=True),
(0, 0x400, 1))
def test_add_window_dense(self):
memory_map = MemoryMap(addr_width=16, data_width=32)
- self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10, data_width=8),
+ self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10,
+ data_width=8),
sparse=False),
(0, 0x100, 4))
def test_add_window_wrong_wider(self):
memory_map = MemoryMap(addr_width=16, data_width=8)
with self.assertRaisesRegex(ValueError,
- r"Window has data width 16, and cannot be added to a memory map "
- r"with data width 8"):
+ r"Window has data width 16, and cannot be added to a memory "
+ r"map with data width 8"):
memory_map.add_window(MemoryMap(addr_width=10, data_width=16))
def test_add_window_wrong_no_mode(self):
memory_map = MemoryMap(addr_width=16, data_width=16)
with self.assertRaisesRegex(ValueError,
- r"Address translation mode must be explicitly specified when adding "
- r"a window with data width 8 to a memory map with data width 16"):
+ r"Address translation mode must be explicitly specified "
+ r"when adding a window with data width 8 to a memory map "
+ r"with data width 16"):
memory_map.add_window(MemoryMap(addr_width=10, data_width=8))
def test_add_window_wrong_ratio(self):
memory_map = MemoryMap(addr_width=16, data_width=16)
with self.assertRaisesRegex(ValueError,
- r"Dense addressing cannot be used because the memory map data width "
- r"16 is not an integer multiple of window data width 7"):
- memory_map.add_window(MemoryMap(addr_width=10, data_width=7), sparse=False)
+ r"Dense addressing cannot be used because the memory map "
+ r"data width 16 is not an integer multiple of window data "
+ r"width 7"):
+ memory_map.add_window(MemoryMap(addr_width=10, data_width=7),
+ sparse=False)
def test_add_window_wrong_overlap(self):
memory_map = MemoryMap(addr_width=16, data_width=8)
memory_map.add_window(MemoryMap(addr_width=10, data_width=8))
with self.assertRaisesRegex(ValueError,
r"Address range 0x200\.\.0x600 overlaps with window "
- r"<nmigen_soc\.memory\.MemoryMap object at .+?> at 0x0\.\.0x400"):
- memory_map.add_window(MemoryMap(addr_width=10, data_width=8), addr=0x200)
+ r"<nmigen_soc\.memory\.MemoryMap object at .+?> "
+ r"at 0x0\.\.0x400"):
+ memory_map.add_window(MemoryMap(addr_width=10, data_width=8),
+ addr=0x200)
def test_add_window_wrong_twice(self):
memory_map = MemoryMap(addr_width=16, data_width=8)
window = MemoryMap(addr_width=10, data_width=8)
memory_map.add_window(window)
with self.assertRaisesRegex(ValueError,
- r"Window <nmigen_soc\.memory\.MemoryMap object at .+?> is already added "
- r"at address range 0x0\.\.0x400"):
+ r"Window <nmigen_soc\.memory\.MemoryMap object at .+?> is "
+ r"already added at address range 0x0\.\.0x400"):
memory_map.add_window(window)
def test_iter_windows(self):
def test_features(self):
iface = Interface(addr_width=32, data_width=32,
- features={"rty", "err", "stall", "lock", "cti", "bte"})
+ features={"rty", "err", "stall", "lock",
+ "cti", "bte"})
self.assertEqual(iface.layout, Layout.cast([
("adr", 32, DIR_FANOUT),
("dat_w", 32, DIR_FANOUT),
def test_add_wrong(self):
with self.assertRaisesRegex(TypeError,
- r"Subordinate bus must be an instance of wishbone\.Interface, not 'foo'"):
+ r"Subordinate bus must be an instance of "
+ r"wishbone\.Interface, not 'foo'"):
self.dut.add("foo")
def test_add_wrong_granularity(self):
with self.assertRaisesRegex(ValueError,
r"Subordinate bus has granularity 32, which is greater than "
r"the decoder granularity 16"):
- self.dut.add(Interface(addr_width=15, data_width=32, granularity=32))
+ self.dut.add(Interface(addr_width=15, data_width=32,
+ granularity=32))
def test_add_wrong_width_dense(self):
with self.assertRaisesRegex(ValueError,
- r"Subordinate bus has data width 16, which is not the same as decoder "
+ r"Subordinate bus has data width 16, which is not the "
+ r"same as decoder "
r"data width 32 \(required for dense address translation\)"):
- self.dut.add(Interface(addr_width=15, data_width=16, granularity=16))
+ self.dut.add(Interface(addr_width=15, data_width=16,
+ granularity=16))
def test_add_wrong_granularity_sparse(self):
with self.assertRaisesRegex(ValueError,
- r"Subordinate bus has data width 64, which is not the same as subordinate "
- r"bus granularity 16 \(required for sparse address translation\)"):
- self.dut.add(Interface(addr_width=15, data_width=64, granularity=16), sparse=True)
+ r"Subordinate bus has data width 64, which is not the same "
+ r"as subordinate bus granularity 16 \(required for "
+ r"sparse address translation\)"):
+ self.dut.add(Interface(addr_width=15, data_width=64,
+ granularity=16), sparse=True)
def test_add_wrong_optional_output(self):
with self.assertRaisesRegex(ValueError,
- r"Subordinate bus has optional output 'err', but the decoder does "
+ r"Subordinate bus has optional output 'err', "
+ r"but the decoder does "
r"not have a corresponding input"):
- self.dut.add(Interface(addr_width=15, data_width=32, granularity=16, features={"err"}))
+ self.dut.add(Interface(addr_width=15, data_width=32,
+ granularity=16, features={"err"}))
class DecoderSimulationTestCase(unittest.TestCase):
self.assertEqual(dut.add(sub_1, addr=0x10000),
(0x10000, 0x20000, 1))
sub_2 = Interface(addr_width=14, data_width=32, granularity=8,
- features={"err", "rty", "stall", "lock", "cti", "bte"})
+ features={"err", "rty", "stall", "lock",
+ "cti", "bte"})
self.assertEqual(dut.add(sub_2),
(0x20000, 0x30000, 1))
for index, sel_bit in enumerate(self.bus.sel):
with m.If(sel_bit):
- segment = self.bus.dat_r.word_select(index, self.bus.granularity)
+ segment = self.bus.dat_r.word_select(index,
+ self.bus.granularity)
m.d.comb += segment.eq(self.bus.adr + index)
return m
def test_add_wrong(self):
with self.assertRaisesRegex(TypeError,
- r"Initiator bus must be an instance of wishbone\.Interface, not 'foo'"):
+ r"Initiator bus must be an instance of "
+ r"wishbone\.Interface, not 'foo'"):
self.dut.add("foo")
def test_add_wrong_addr_width(self):
with self.assertRaisesRegex(ValueError,
- r"Initiator bus has address width 15, which is not the same as arbiter "
+ r"Initiator bus has address width 15, which is "
+ r"not the same as arbiter "
r"address width 31"):
- self.dut.add(Interface(addr_width=15, data_width=32, granularity=16))
+ self.dut.add(Interface(addr_width=15, data_width=32,
+ granularity=16))
def test_add_wrong_granularity(self):
with self.assertRaisesRegex(ValueError,
r"Initiator bus has granularity 8, which is lesser than "
r"the arbiter granularity 16"):
- self.dut.add(Interface(addr_width=31, data_width=32, granularity=8))
+ self.dut.add(Interface(addr_width=31, data_width=32,
+ granularity=8))
def test_add_wrong_data_width(self):
with self.assertRaisesRegex(ValueError,
- r"Initiator bus has data width 16, which is not the same as arbiter "
+ r"Initiator bus has data width 16, which is not "
+ r"the same as arbiter "
r"data width 32"):
- self.dut.add(Interface(addr_width=31, data_width=16, granularity=16))
+ self.dut.add(Interface(addr_width=31, data_width=16,
+ granularity=16))
def test_add_wrong_optional_output(self):
with self.assertRaisesRegex(ValueError,
- r"Initiator bus has optional output 'lock', but the arbiter does "
+ r"Initiator bus has optional output 'lock', but "
+ r"the arbiter does "
r"not have a corresponding input"):
self.dut.add(Interface(addr_width=31, data_width=32, granularity=16,
features={"lock"}))
itor_1 = Interface(addr_width=30, data_width=32, granularity=8)
dut.add(itor_1)
itor_2 = Interface(addr_width=30, data_width=32, granularity=16,
- features={"err", "rty", "stall", "lock", "cti", "bte"})
+ features={"err", "rty", "stall", "lock",
+ "cti", "bte"})
dut.add(itor_2)
def sim_test():
class Interface(Record):
"""Wishbone interface.
- See the `Wishbone specification <https://opencores.org/howto/wishbone>`_ for description
- of the Wishbone signals. The ``RST_I`` and ``CLK_I`` signals are provided as a part of
- the clock domain that drives the interface.
+ See the `Wishbone specification
+ <https://opencores.org/howto/wishbone>`_ for description of the
+ Wishbone signals. The ``RST_I`` and ``CLK_I`` signals are provided
+ as a part of the clock domain that drives the interface.
- Note that the data width of the underlying memory map of the interface is equal to port
- granularity, not port size. If port granularity is less than port size, then the address width
- of the underlying memory map is extended to reflect that.
+ Note that the data width of the underlying memory map of the interface
+ is equal to port granularity, not port size. If port granularity is
+ less than port size, then the address width of the underlying memory
+ map is extended to reflect that.
Parameters
----------
Width of the data signals ("port size" in Wishbone terminology).
One of 8, 16, 32, 64.
granularity : int or None
- Granularity of select signals ("port granularity" in Wishbone terminology).
- One of 8, 16, 32, 64. Optional and defaults to None, meaning it is equal
- to the address width.
+ Granularity of select signals ("port granularity" in Wishbone
+ terminology). One of 8, 16, 32, 64. Optional and defaults to
+ None, meaning it is equal to the address width.
features : iter(str)
Selects the optional signals that will be a part of this interface.
alignment : int
Attributes
----------
- The correspondence between the nMigen-SoC signals and the Wishbone signals changes depending
- on whether the interface acts as an initiator or a target.
+ The correspondence between the nMigen-SoC signals and the Wishbone
+ signals changes depending on whether the interface acts as an
+ initiator or a target.
adr : Signal(addr_width)
- Corresponds to Wishbone signal ``ADR_O`` (initiator) or ``ADR_I`` (target).
+ Corresponds to Wishbone signal ``ADR_O`` (initiator) or ``ADR_I``
+ (target).
dat_w : Signal(data_width)
- Corresponds to Wishbone signal ``DAT_O`` (initiator) or ``DAT_I`` (target).
+ Corresponds to Wishbone signal ``DAT_O`` (initiator) or ``DAT_I``
+ (target).
dat_r : Signal(data_width)
- Corresponds to Wishbone signal ``DAT_I`` (initiator) or ``DAT_O`` (target).
+ Corresponds to Wishbone signal ``DAT_I`` (initiator) or ``DAT_O``
+ (target).
sel : Signal(data_width // granularity)
- Corresponds to Wishbone signal ``SEL_O`` (initiator) or ``SEL_I`` (target).
+ Corresponds to Wishbone signal ``SEL_O`` (initiator) or ``SEL_I``
+ (target).
cyc : Signal()
- Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I`` (target).
+ Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I``
+ (target).
stb : Signal()
- Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I`` (target).
+ Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I``
+ (target).
we : Signal()
- Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I`` (target).
+ Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I``
+ (target).
ack : Signal()
- Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O`` (target).
+ Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O``
+ (target).
err : Signal()
- Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator) or ``ERR_O`` (target).
+ Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator)
+ or ``ERR_O`` (target).
rty : Signal()
- Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator) or ``RTY_O`` (target).
+ Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator)
+ or ``RTY_O`` (target).
stall : Signal()
- Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator) or ``STALL_O`` (target).
+ Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator)
+ or ``STALL_O`` (target).
lock : Signal()
- Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator) or ``LOCK_I`` (target).
+ Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator)
+ or ``LOCK_I`` (target).
cti : Signal()
- Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator) or ``CTI_I`` (target).
+ Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator)
+ or ``CTI_I`` (target).
bte : Signal()
- Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator) or ``BTE_I`` (target).
+ Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator)
+ or ``BTE_I`` (target).
"""
def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(),
alignment=0, name=None):
def add(self, sub_bus, *, addr=None, sparse=False):
"""Add a window to a subordinate bus.
- The decoder can perform either sparse or dense address translation. If dense address
- translation is used (the default), the subordinate bus must have the same data width as
- the decoder; the window will be contiguous. If sparse address translation is used,
- the subordinate bus may have data width less than the data width of the decoder;
- the window may be discontiguous. In either case, the granularity of the subordinate bus
- must be equal to or less than the granularity of the decoder.
+ The decoder can perform either sparse or dense address
+ translation. If dense address translation is used (the default),
+ the subordinate bus must have the same data width as the decoder;
+ the window will be contiguous. If sparse address translation is
+ used, the subordinate bus may have data width less than the data
+ width of the decoder; the window may be discontiguous. In either
+ case, the granularity of the subordinate bus must be equal to
+ or less than the granularity of the decoder.
See :meth:`MemoryMap.add_resource` for details.
"""
features : iter(str)
Optional signal set for the shared bus. See :class:`Interface`.
scheduler : str
- Method for bus arbitration. Optional and defaults to "rr" (Round Robin, see
- :class:`scheduler.RoundRobin`).
+ Method for bus arbitration. Optional and defaults to "rr"
+ (Round Robin, see :class:`scheduler.RoundRobin`).
Attributes
----------
def add(self, itor_bus):
"""Add an initiator bus to the arbiter.
- The initiator bus must have the same address width and data width as the arbiter. The
- granularity of the initiator bus must be greater than or equal to the granularity of
- the arbiter.
+ The initiator bus must have the same address width and data
+ width as the arbiter. The granularity of the initiator bus must
+ be greater than or equal to the granularity of the arbiter.
"""
if not isinstance(itor_bus, Interface):
raise TypeError("Initiator bus must be an instance of wishbone.Interface, not {!r}"
If the item is a :class:`Record`, its fields must be named using the
convention of :class:`Interface`.
targets : list of (:class:`Interface` OR tuple of (:class:`Interface`, int))
- List of SLAVEs on the decoder whose accesses are to be targeted by the shared bus.
- If the item is a tuple of (intf, addr), the :class:`Interface`-type intf is added
- at the (address width + granularity bits)-wide address of the int-type addr.
+ List of SLAVEs on the decoder whose accesses are to be targeted
+ by the shared bus. If the item is a tuple of (intf, addr), the
+ :class:`Interface`-type intf is added at the (address width +
+ granularity bits)-wide address of the int-type addr.
granularity : int or None
Granularity of the shared bus. Optional. See :class:`Interface`.
features : iter(str)
Optional signal set for the shared bus. See :class:`Interface`.
scheduler : str
- Method for bus arbitration for the arbiter. Optional and defaults to "rr" (Round Robin, see
- :class:`scheduler.RoundRobin`).
+ Method for bus arbitration for the arbiter. Optional and defaults
+ to "rr" (Round Robin, see :class:`scheduler.RoundRobin`).
alignment : int
Window alignment for the decoder. Optional. See :class:`Interface`.
class SRAM(Elaboratable):
- """SRAM module carrying a volatile memory block (implemented with :class:`Memory`)
- that can be read and write (or only read if the SRAM is read-only) through a Wishbone bus.
+ """SRAM module carrying a volatile memory block (implemented with
+ :class:`Memory`) that can be read and write (or only read if the
+ SRAM is read-only) through a Wishbone bus.
- If no Wishbone bus is specified during initialisation, this creates one whose address width
- is just enough to fit the whole memory (i.e. equals to the log2(memory depth) rounded up), and
- whose data width is equal to the memory width.
+ If no Wishbone bus is specified during initialisation, this creates
+ one whose address width is just enough to fit the whole memory
+ (i.e. equals to the log2(memory depth) rounded up), and whose data
+ width is equal to the memory width.
Parameters
----------
read_only : bool
Whether or not the memory is read-only. Defaults to False.
bus : :class:`Interface` or None
- The Wishbone bus interface providing access to the read/write ports of the memory.
- Optional and defaults to None, which lets this module to instantiate one as described
- above, having the granularity, features and alignment as specified by their
+ The Wishbone bus interface providing access to the read/write
+ ports of the memory. Optional and defaults to None, which
+ lets this module to instantiate one as described above, having
+ the granularity, features and alignment as specified by their
corresponding parameters.
granularity : int or None
- If the Wishbone bus is not sepcified, this is the granularity of the Wishbone bus.
- Optional. See :class:`Interface`.
+ If the Wishbone bus is not sepcified, this is the granularity
+ of the Wishbone bus. Optional. See :class:`Interface`.
features : iter(str)
- If the Wishbone bus is not sepcified, this is the optional signal set for the Wishbone bus.
- See :class:`Interface`.
+ If the Wishbone bus is not sepcified, this is the optional signal
+ set for the Wishbone bus. See :class:`Interface`.
Attributes
----------
memory : :class:`Memory`
The memory to be accessed via the Wishbone bus.
bus : :class:`Interface`
- The Wishbone bus interface providing access to the read/write ports of the memory.
+ The Wishbone bus interface providing access to the read/write
+ ports of the memory.
"""
def __init__(self, memory, read_only=False, bus=None,
granularity=None, features=frozenset()):
with m.If(self.bus.cyc & self.bus.stb & ~self.bus.ack):
m.d.sync += self.bus.ack.eq(1)
- return m
\ No newline at end of file
+ return m