Skip to content

Commit cade7f7

Browse files
committed
major refactor of memory (removes _MemBase class entirely)
1 parent 1afbe7e commit cade7f7

File tree

6 files changed

+70
-94
lines changed

6 files changed

+70
-94
lines changed

docs/index.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,6 @@ overloaded operations such as `addition` or `bitwise or`. A bunch of other relat
9090
* :py:class:`~pyrtl.wire.Const` (WireVector)
9191
* :py:class:`~pyrtl.wire.Register` (WireVector)
9292

93-
* Memory blocks [base class for internal use only]
94-
* :py:class:`~pyrtl.memory.MemBlock` (_MemReadBase)
95-
* :py:class:`~pyrtl.memory.RomBlock` (_MemReadBase)
96-
9793
After specifying a hardware design, there are then options to simulate your design right in PyRTL,
9894
synthesize it down to primitive 1-bit operations, optimize it, and export it to Verilog (along with
9995
a testbench),.

pyrtl/analysis/estimate.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,8 @@ def _bits_ports_and_isrom_from_memory(mem):
122122
is_rom = False
123123
bits = 2**mem.addrwidth * mem.bitwidth
124124
read_ports = len(mem.readport_nets)
125-
try:
126-
write_ports = len(mem.writeport_nets)
127-
except AttributeError: # dealing with ROMs
128-
if not isinstance(mem, RomBlock):
129-
raise PyrtlInternalError('Mem with no writeport_nets attribute'
130-
' but not a ROM? Thats an error')
131-
write_ports = 0
125+
write_ports = len(mem.writeport_nets)
126+
if isinstance(mem, RomBlock):
132127
is_rom = True
133128
ports = max(read_ports, write_ports)
134129
return bits, ports, is_rom

pyrtl/compilesim.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ def _create_code(self, write):
705705
self._declare_mem_helpers(write)
706706
roms = {mem for mem in mems if isinstance(mem, RomBlock)}
707707
self._declare_roms(write, roms)
708-
mems = {mem for mem in mems if isinstance(mem, MemBlock)}
708+
mems = {mem for mem in mems if isinstance(mem, MemBlock) and not isinstance(mem, RomBlock)}
709709
self._declare_mems(write, mems)
710710

711711
# single step function

pyrtl/core.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -617,16 +617,16 @@ def sanity_check_wirevector(self, w):
617617

618618
def sanity_check_memblock(self, m):
619619
""" Check that m is a valid memblock type. """
620-
from .memory import _MemReadBase
621-
if not isinstance(m, _MemReadBase):
620+
from .memory import MemBlock
621+
if not isinstance(m, MemBlock):
622622
raise PyrtlError(
623623
'error attempting to pass an input of type "%s" '
624-
'instead of _MemReadBase' % type(m))
624+
'instead of MemBlock' % type(m))
625625

626626
def sanity_check_net(self, net):
627627
""" Check that net is a valid LogicNet. """
628628
from .wire import Input, Output, Const, Register
629-
from .memory import _MemReadBase
629+
from .memory import MemBlock
630630

631631
# general sanity checks that apply to all operations
632632
if not isinstance(net, LogicNet):
@@ -697,7 +697,7 @@ def sanity_check_net(self, net):
697697
raise PyrtlInternalError('error, mem op requires 2 op_params in tuple')
698698
if not isinstance(net.op_param[0], int):
699699
raise PyrtlInternalError('error, mem op requires first operand as int')
700-
if not isinstance(net.op_param[1], _MemReadBase):
700+
if not isinstance(net.op_param[1], MemBlock):
701701
raise PyrtlInternalError('error, mem op requires second operand of a memory type')
702702

703703
# operation-specific checks on destinations

pyrtl/memory.py

Lines changed: 56 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -87,68 +87,7 @@ def name(self, n):
8787
as_wires(self).name = n
8888

8989

90-
class _MemReadBase(object):
91-
""" This is the base class for the memories and ROM blocks and
92-
it implements the read and initialization operations needed for
93-
both of them.
94-
"""
95-
96-
# FIXME: right now read port is built unconditionally (no read enable)
97-
98-
def __init__(self, bitwidth, addrwidth, name, max_read_ports, asynchronous, block):
99-
self.max_read_ports = max_read_ports
100-
self.read_ports = 0
101-
self.block = working_block(block)
102-
name = next_tempvar_name(name)
103-
104-
if bitwidth <= 0:
105-
raise PyrtlError('bitwidth must be >= 1')
106-
if addrwidth <= 0:
107-
raise PyrtlError('addrwidth must be >= 1')
108-
109-
self.bitwidth = bitwidth
110-
self.name = name
111-
self.addrwidth = addrwidth
112-
self.readport_nets = []
113-
self.id = _memIndex.next_index()
114-
self.asynchronous = asynchronous
115-
self.block._add_memblock(self)
116-
117-
def __getitem__(self, item):
118-
""" Builds circuitry to retrieve an item from the memory """
119-
item = as_wires(item, bitwidth=self.addrwidth, truncating=False)
120-
if len(item) > self.addrwidth:
121-
raise PyrtlError('memory index bitwidth > addrwidth')
122-
return _MemIndexed(mem=self, index=item)
123-
124-
def _readaccess(self, addr):
125-
# FIXME: add conditional read ports
126-
return self._build_read_port(addr)
127-
128-
def _build_read_port(self, addr):
129-
if self.max_read_ports is not None:
130-
self.read_ports += 1
131-
if self.read_ports > self.max_read_ports:
132-
raise PyrtlError('maximum number of read ports (%d) exceeded' % self.max_read_ports)
133-
data = WireVector(bitwidth=self.bitwidth)
134-
readport_net = LogicNet(
135-
op='m',
136-
op_param=(self.id, self),
137-
args=(addr,),
138-
dests=(data,))
139-
working_block().add_net(readport_net)
140-
self.readport_nets.append(readport_net)
141-
return data
142-
143-
def __setitem__(self, key, value):
144-
""" Not legal on a object that only allows for reads"""
145-
raise PyrtlError("error, invalid call __setitem__ made on _MemReadBase")
146-
147-
def _make_copy(self, block):
148-
pass
149-
150-
151-
class MemBlock(_MemReadBase):
90+
class MemBlock(object):
15291
""" MemBlock is the object for specifying block memories. It can be
15392
indexed like an array for both reading and writing. Writes under a conditional
15493
are automatically converted to enabled writes. For example, consider the following
@@ -205,19 +144,61 @@ def __init__(self, bitwidth, addrwidth, name='', max_read_ports=2, max_write_por
205144
that memories with high numbers of ports may not be possible to map to physical memories
206145
such as block rams or existing memory hardware macros.
207146
"""
208-
super(MemBlock, self).__init__(bitwidth, addrwidth, name, max_read_ports,
209-
asynchronous, block)
147+
self.max_read_ports = max_read_ports
148+
self.num_read_ports = 0
149+
self.block = working_block(block)
150+
name = next_tempvar_name(name)
151+
152+
if bitwidth <= 0:
153+
raise PyrtlError('bitwidth must be >= 1')
154+
if addrwidth <= 0:
155+
raise PyrtlError('addrwidth must be >= 1')
156+
157+
self.bitwidth = bitwidth
158+
self.name = name
159+
self.addrwidth = addrwidth
160+
self.readport_nets = []
161+
self.id = _memIndex.next_index()
162+
self.asynchronous = asynchronous
163+
self.block._add_memblock(self)
164+
210165
self.max_write_ports = max_write_ports
211-
self.write_ports = 0
166+
self.num_write_ports = 0
212167
self.writeport_nets = []
213168

169+
def __getitem__(self, item):
170+
""" Builds circuitry to retrieve an item from the memory """
171+
item = as_wires(item, bitwidth=self.addrwidth, truncating=False)
172+
if len(item) > self.addrwidth:
173+
raise PyrtlError('memory index bitwidth > addrwidth')
174+
return _MemIndexed(mem=self, index=item)
175+
214176
def __setitem__(self, item, assignment):
215177
""" Builds circuitry to set an item in the memory """
216178
if isinstance(assignment, _MemAssignment):
217179
self._assignment(item, assignment.rhs, is_conditional=assignment.is_conditional)
218180
else:
219181
raise PyrtlError('error, assigment to memories should use "<<=" not "=" operator')
220182

183+
def _readaccess(self, addr):
184+
# FIXME: add conditional read ports
185+
return self._build_read_port(addr)
186+
187+
def _build_read_port(self, addr):
188+
if self.max_read_ports is not None:
189+
self.num_read_ports += 1
190+
if self.num_read_ports > self.max_read_ports:
191+
raise PyrtlError('maximum number of read ports (%d) exceeded' % self.max_read_ports)
192+
data = WireVector(bitwidth=self.bitwidth)
193+
readport_net = LogicNet(
194+
op='m',
195+
op_param=(self.id, self),
196+
args=(addr,),
197+
dests=(data,))
198+
working_block().add_net(readport_net)
199+
self.readport_nets.append(readport_net)
200+
return data
201+
221202
def _assignment(self, item, val, is_conditional):
222203
from .conditional import _build
223204

@@ -246,8 +227,8 @@ def _assignment(self, item, val, is_conditional):
246227
def _build(self, addr, data, enable):
247228
""" Builds a write port. """
248229
if self.max_write_ports is not None:
249-
self.write_ports += 1
250-
if self.write_ports > self.max_write_ports:
230+
self.num_write_ports += 1
231+
if self.num_write_ports > self.max_write_ports:
251232
raise PyrtlError('maximum number of write ports (%d) exceeded' %
252233
self.max_write_ports)
253234
writeport_net = LogicNet(
@@ -269,7 +250,7 @@ def _make_copy(self, block=None):
269250
block=block)
270251

271252

272-
class RomBlock(_MemReadBase):
253+
class RomBlock(MemBlock):
273254
""" PyRTL Read Only Memory.
274255
275256
RomBlocks are the read only memory block for PyRTL. They support the same read interface
@@ -302,8 +283,9 @@ def __init__(self, bitwidth, addrwidth, romdata, name='', max_read_ports=2,
302283
:param block: The block to add to, defaults to the working block
303284
"""
304285

305-
super(RomBlock, self).__init__(bitwidth, addrwidth, name, max_read_ports,
306-
asynchronous, block)
286+
super(RomBlock, self).__init__(bitwidth=bitwidth, addrwidth=addrwidth, name=name,
287+
max_read_ports=max_read_ports, max_write_ports=0,
288+
asynchronous=asynchronous, block=block)
307289
self.data = romdata
308290
self.build_new_roms = build_new_roms
309291
self.current_copy = self
@@ -317,6 +299,9 @@ def __getitem__(self, item):
317299
# If you really know what you are doing, use a Const WireVector instead.
318300
return super(RomBlock, self).__getitem__(item)
319301

302+
def __setitem__(self, item, assignment):
303+
raise PyrtlError('no writing to a read-only memory')
304+
320305
def _get_read_data(self, address):
321306
import types
322307
try:
@@ -361,7 +346,7 @@ def _get_read_data(self, address):
361346

362347
def _build_read_port(self, addr):
363348
if self.build_new_roms and \
364-
(self.current_copy.read_ports >= self.current_copy.max_read_ports):
349+
(self.current_copy.num_read_ports >= self.current_copy.max_read_ports):
365350
self.current_copy = self._make_copy()
366351
return super(RomBlock, self.current_copy)._build_read_port(addr)
367352

tests/test_memblock.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def test_read_memindexed_ilshift(self):
129129
})
130130
self.assertEqual(sim.inspect(y), 5 - i)
131131
self.assertEqual(sim.inspect(z), 5 - i)
132-
self.assertEqual(self.mem.read_ports, 1)
132+
self.assertEqual(self.mem.num_read_ports, 1)
133133

134134
def test_write_memindexed_ilshift(self):
135135
self.mem1 = pyrtl.MemBlock(8, 8)
@@ -151,8 +151,8 @@ def test_write_memindexed_ilshift(self):
151151
inp: 5 - i
152152
})
153153
self.assertEqual(sim.inspect(out), 0 if i == 0 else 5 - (i - 1))
154-
self.assertEqual(self.mem1.read_ports, 1) # 2 b/c of the output read
155-
self.assertEqual(self.mem2.write_ports, 1)
154+
self.assertEqual(self.mem1.num_read_ports, 1) # 2 b/c of the output read
155+
self.assertEqual(self.mem2.num_write_ports, 1)
156156

157157
def test_read_memindexed_ior(self):
158158
self.mem = pyrtl.MemBlock(8, 8)
@@ -189,7 +189,7 @@ def test_read_memindexed_ior(self):
189189
self.assertEqual(sim.inspect(y), y_exp)
190190
self.assertEqual(sim.inspect(z), z_exp)
191191
self.assertEqual(sim.inspect(w), w_exp)
192-
self.assertEqual(self.mem.read_ports, 1)
192+
self.assertEqual(self.mem.num_read_ports, 1)
193193

194194
def test_write_memindexed_ior(self):
195195
self.mem1 = pyrtl.MemBlock(8, 8)
@@ -225,8 +225,8 @@ def test_write_memindexed_ior(self):
225225
else:
226226
out_exp = 2
227227
self.assertEqual(sim.inspect(out), out_exp)
228-
self.assertEqual(self.mem1.read_ports, 1)
229-
self.assertEqual(self.mem2.write_ports, 1)
228+
self.assertEqual(self.mem1.num_read_ports, 1)
229+
self.assertEqual(self.mem2.num_write_ports, 1)
230230

231231

232232
class RTLRomBlockWiring(unittest.TestCase):

0 commit comments

Comments
 (0)