From f1a8aa5206afeee7ced490a719bd671c5c247cf4 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Sat, 20 Jun 2020 00:08:45 +0100 Subject: [PATCH] whitespace cleanup --- nmigen_soc/csr/bus.py | 134 ++++++++++++--------- nmigen_soc/csr/wishbone.py | 13 ++- nmigen_soc/memory.py | 168 +++++++++++++++------------ nmigen_soc/scheduler.py | 11 +- nmigen_soc/test/test_csr_bus.py | 2 +- nmigen_soc/test/test_csr_wishbone.py | 3 +- nmigen_soc/test/test_memory.py | 50 ++++---- nmigen_soc/test/test_wishbone_bus.py | 59 ++++++---- nmigen_soc/wishbone/bus.py | 102 +++++++++------- nmigen_soc/wishbone/sram.py | 32 ++--- 10 files changed, 338 insertions(+), 236 deletions(-) diff --git a/nmigen_soc/csr/bus.py b/nmigen_soc/csr/bus.py index 0786a9e..e760197 100644 --- a/nmigen_soc/csr/bus.py +++ b/nmigen_soc/csr/bus.py @@ -12,8 +12,9 @@ class Element(Record): 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" @@ -27,9 +28,10 @@ class Element(Record): """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 ---------- @@ -43,15 +45,16 @@ class Element(Record): 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: @@ -83,25 +86,29 @@ class Element(Record): 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 @@ -116,20 +123,23 @@ class Interface(Record): 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): @@ -161,30 +171,38 @@ class Multiplexer(Elaboratable): 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 ---------- @@ -282,14 +300,16 @@ class Decoder(Elaboratable): 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 ---------- diff --git a/nmigen_soc/csr/wishbone.py b/nmigen_soc/csr/wishbone.py index e9150b5..e20c85d 100644 --- a/nmigen_soc/csr/wishbone.py +++ b/nmigen_soc/csr/wishbone.py @@ -11,21 +11,24 @@ __all__ = ["WishboneCSRBridge"] 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 ---------- diff --git a/nmigen_soc/memory.py b/nmigen_soc/memory.py index b3a3386..4f130c5 100644 --- a/nmigen_soc/memory.py +++ b/nmigen_soc/memory.py @@ -48,19 +48,21 @@ class _RangeMap: 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 ---------- @@ -69,9 +71,9 @@ class MemoryMap: 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: @@ -106,8 +108,8 @@ class MemoryMap: 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 ------------ @@ -162,31 +164,35 @@ class MemoryMap: 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] @@ -214,7 +220,8 @@ class MemoryMap: 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) @@ -222,47 +229,56 @@ class MemoryMap: 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}" @@ -313,10 +329,11 @@ class MemoryMap: 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) @@ -328,11 +345,13 @@ class MemoryMap: 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(): @@ -355,15 +374,15 @@ class MemoryMap: 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: @@ -377,8 +396,9 @@ class MemoryMap: 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 --------- @@ -387,10 +407,11 @@ class MemoryMap: 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 ---------- @@ -418,7 +439,8 @@ class MemoryMap: 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: diff --git a/nmigen_soc/scheduler.py b/nmigen_soc/scheduler.py index 779e95c..588238a 100644 --- a/nmigen_soc/scheduler.py +++ b/nmigen_soc/scheduler.py @@ -15,11 +15,14 @@ class RoundRobin(Elaboratable): 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 @@ -41,4 +44,4 @@ class RoundRobin(Elaboratable): with m.If(self.request[t]): m.d.sync += self.grant.eq(t) - return m \ No newline at end of file + return m diff --git a/nmigen_soc/test/test_csr_bus.py b/nmigen_soc/test/test_csr_bus.py index bd3656e..12fb580 100644 --- a/nmigen_soc/test/test_csr_bus.py +++ b/nmigen_soc/test/test_csr_bus.py @@ -271,7 +271,7 @@ class DecoderTestCase(unittest.TestCase): 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): diff --git a/nmigen_soc/test/test_csr_wishbone.py b/nmigen_soc/test/test_csr_wishbone.py index 48dd43f..a754d18 100644 --- a/nmigen_soc/test/test_csr_wishbone.py +++ b/nmigen_soc/test/test_csr_wishbone.py @@ -38,7 +38,8 @@ class WishboneCSRBridgeTestCase(unittest.TestCase): 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) diff --git a/nmigen_soc/test/test_memory.py b/nmigen_soc/test/test_memory.py index 1e61511..040f777 100644 --- a/nmigen_soc/test/test_memory.py +++ b/nmigen_soc/test/test_memory.py @@ -86,7 +86,8 @@ class MemoryMapTestCase(unittest.TestCase): 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): @@ -104,19 +105,20 @@ class MemoryMapTestCase(unittest.TestCase): 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): @@ -138,19 +140,22 @@ class MemoryMapTestCase(unittest.TestCase): 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)) @@ -163,39 +168,44 @@ class MemoryMapTestCase(unittest.TestCase): 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" at 0x0\.\.0x400"): - memory_map.add_window(MemoryMap(addr_width=10, data_width=8), addr=0x200) + r" " + 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 is already added " - r"at address range 0x0\.\.0x400"): + r"Window is " + r"already added at address range 0x0\.\.0x400"): memory_map.add_window(window) def test_iter_windows(self): diff --git a/nmigen_soc/test/test_wishbone_bus.py b/nmigen_soc/test/test_wishbone_bus.py index 63d4109..7360edc 100644 --- a/nmigen_soc/test/test_wishbone_bus.py +++ b/nmigen_soc/test/test_wishbone_bus.py @@ -47,7 +47,8 @@ class InterfaceTestCase(unittest.TestCase): 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), @@ -104,32 +105,40 @@ class DecoderTestCase(unittest.TestCase): 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): @@ -140,7 +149,8 @@ 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)) @@ -228,7 +238,8 @@ class DecoderSimulationTestCase(unittest.TestCase): 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 @@ -338,30 +349,37 @@ class ArbiterTestCase(unittest.TestCase): 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"})) @@ -374,7 +392,8 @@ class ArbiterSimulationTestCase(unittest.TestCase): 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(): diff --git a/nmigen_soc/wishbone/bus.py b/nmigen_soc/wishbone/bus.py index 039413d..ba5bc86 100644 --- a/nmigen_soc/wishbone/bus.py +++ b/nmigen_soc/wishbone/bus.py @@ -30,13 +30,15 @@ class BurstTypeExt(Enum): class Interface(Record): """Wishbone interface. - See the `Wishbone specification `_ 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 + `_ 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 ---------- @@ -46,9 +48,9 @@ class Interface(Record): 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 @@ -58,37 +60,52 @@ class Interface(Record): 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): @@ -216,12 +233,14 @@ class Decoder(Elaboratable): 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. """ @@ -319,8 +338,8 @@ class Arbiter(Elaboratable): 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 ---------- @@ -340,9 +359,9 @@ class Arbiter(Elaboratable): 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}" @@ -452,16 +471,17 @@ class InterconnectShared(Elaboratable): 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`. diff --git a/nmigen_soc/wishbone/sram.py b/nmigen_soc/wishbone/sram.py index de5c15d..eeeada3 100644 --- a/nmigen_soc/wishbone/sram.py +++ b/nmigen_soc/wishbone/sram.py @@ -8,12 +8,14 @@ __all__ = ["SRAM"] 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 ---------- @@ -22,23 +24,25 @@ class SRAM(Elaboratable): 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()): @@ -86,4 +90,4 @@ class SRAM(Elaboratable): 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 -- 2.30.2