parameterise I-Cache similar to D-Cache. lots of "self."
[soc.git] / src / soc / bus / sram.py
1 from nmigen import Elaboratable, Memory, Module, Signal
2 from nmigen.utils import log2_int
3
4 from nmigen_soc.wishbone.bus import Interface
5
6
7 __all__ = ["SRAM"]
8
9
10 class SRAM(Elaboratable):
11 """SRAM module carrying a volatile memory block (implemented with
12 :class:`Memory`) that can be read and write (or only read if the
13 SRAM is read-only) through a Wishbone bus.
14
15 If no Wishbone bus is specified during initialisation, this creates
16 one whose address width is just enough to fit the whole memory
17 (i.e. equals to the log2(memory depth) rounded up), and whose data
18 width is equal to the memory width.
19
20 Parameters
21 ----------
22 memory : :class:`Memory`
23 The memory to be accessed via the Wishbone bus.
24 read_only : bool
25 Whether or not the memory is read-only. Defaults to False.
26 bus : :class:`Interface` or None
27 The Wishbone bus interface providing access to the read/write
28 ports of the memory. Optional and defaults to None, which
29 lets this module to instantiate one as described above, having
30 the granularity, features and alignment as specified by their
31 corresponding parameters.
32 granularity : int or None
33 If the Wishbone bus is not specified, this is the granularity
34 of the Wishbone bus. Optional. See :class:`Interface`.
35 features : iter(str)
36 If the Wishbone bus is not specified, this is the optional signal
37 set for the Wishbone bus. See :class:`Interface`.
38
39 Attributes
40 ----------
41 memory : :class:`Memory`
42 The memory to be accessed via the Wishbone bus.
43 bus : :class:`Interface`
44 The Wishbone bus interface providing access to the read/write
45 ports of the memory.
46 """
47
48 def __init__(self, memory, read_only=False, bus=None,
49 granularity=None, features=None):
50 if features is None:
51 features = frozenset()
52 if not isinstance(memory, Memory):
53 raise TypeError("Memory {!r} is not a Memory"
54 .format(memory))
55 self.memory = memory
56 self.read_only = read_only
57 if bus is None:
58 bus = Interface(addr_width=log2_int(self.memory.depth,
59 need_pow2=False),
60 data_width=self.memory.width,
61 granularity=granularity,
62 features=features,
63 #alignment=0,
64 name=None)
65 self.bus = bus
66 self.granularity = bus.granularity
67
68 def elaborate(self, platform):
69 m = Module()
70
71 if self.memory.width > len(self.bus.dat_r):
72 raise NotImplementedError
73
74 # read - this relies on the read port producing data
75 # with one clock delay. the "ack" goes out on a sync
76 # which matches that
77 m.submodules.rdport = rdport = self.memory.read_port()
78 m.d.comb += [
79 rdport.addr.eq(self.bus.adr[:len(rdport.addr)]),
80 self.bus.dat_r.eq(rdport.data)
81 ]
82
83 # write
84 if not self.read_only:
85 m.submodules.wrport = wrport = self.memory.write_port(
86 granularity=self.granularity)
87 m.d.comb += [
88 wrport.addr.eq(self.bus.adr[:len(rdport.addr)]),
89 wrport.data.eq(self.bus.dat_w)
90 ]
91 n_wrport = wrport.en.width
92 n_bussel = self.bus.sel.width
93 assert n_wrport == n_bussel, "bus enable count %d " \
94 "must match memory wen count %d" % (n_wrport, n_bussel)
95 wen = Signal()
96 m.d.comb += wen.eq(self.bus.cyc & self.bus.stb & self.bus.we)
97 with m.If(wen):
98 m.d.comb += wrport.en.eq(self.bus.sel)
99
100 # generate ack (no "pipeline" mode here)
101 m.d.sync += self.bus.ack.eq(0)
102 with m.If(self.bus.cyc & self.bus.stb & ~self.bus.ack):
103 m.d.sync += self.bus.ack.eq(1)
104
105 return m