format mem.py
[openpower-isa.git] / src / openpower / decoder / isa / mem.py
1 # SPDX-License-Identifier: LGPLv3+
2 # Copyright (C) 2020, 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Funded by NLnet http://nlnet.nl
4 """core of the python-based POWER9 simulator
5
6 this is part of a cycle-accurate POWER9 simulator. its primary purpose is
7 not speed, it is for both learning and educational purposes, as well as
8 a method of verifying the HDL.
9
10 related bugs:
11
12 * https://bugs.libre-soc.org/show_bug.cgi?id=424
13 """
14
15 from collections import defaultdict
16 from openpower.decoder.selectable_int import SelectableInt
17 from openpower.util import log, LogKind
18 import math
19
20
21 def swap_order(x, nbytes):
22 x = x.to_bytes(nbytes, byteorder='little')
23 x = int.from_bytes(x, byteorder='big', signed=False)
24 return x
25
26
27 class MemException(Exception):
28 pass
29
30
31 def process_mem(initial_mem, row_bytes=8):
32 res = {}
33 # different types of memory data structures recognised (for convenience)
34 if isinstance(initial_mem, list):
35 initial_mem = (0, initial_mem)
36 if isinstance(initial_mem, tuple):
37 startaddr, mem = initial_mem
38 initial_mem = {}
39 for i, val in enumerate(mem):
40 initial_mem[startaddr + row_bytes*i] = (val, row_bytes)
41
42 for addr, val in initial_mem.items():
43 if isinstance(val, tuple):
44 (val, width) = val
45 else:
46 width = row_bytes # assume same width
47 # val = swap_order(val, width)
48 res[addr] = (val, width)
49
50 return res
51
52
53 class Mem:
54
55 def __init__(self, row_bytes=8, initial_mem=None, misaligned_ok=False):
56 self.mem = {}
57 self.bytes_per_word = row_bytes
58 self.word_log2 = math.ceil(math.log2(row_bytes))
59 self.last_ld_addr = None
60 self.last_st_addr = None
61 self.misaligned_ok = misaligned_ok
62 log("Sim-Mem", initial_mem, self.bytes_per_word, self.word_log2)
63 if not initial_mem:
64 return
65
66 for addr, (val, width) in process_mem(initial_mem, row_bytes).items():
67 # val = swap_order(val, width)
68 self.st(addr, val, width, swap=False)
69
70 def _get_shifter_mask(self, wid, remainder):
71 shifter = ((self.bytes_per_word - wid) - remainder) * \
72 8 # bits per byte
73 # XXX https://bugs.libre-soc.org/show_bug.cgi?id=377
74 # BE/LE mode?
75 shifter = remainder * 8
76 mask = (1 << (wid * 8)) - 1
77 log("width,rem,shift,mask", wid, remainder, hex(shifter), hex(mask))
78 return shifter, mask
79
80 # TODO: Implement ld/st of lesser width
81 def ld(self, address, width=8, swap=True, check_in_mem=False,
82 instr_fetch=False):
83 log("ld from addr 0x%x width %d" % (address, width),
84 swap, check_in_mem, instr_fetch)
85 self.last_ld_addr = address # record last load
86 ldaddr = address
87 remainder = address & (self.bytes_per_word - 1)
88 address = address >> self.word_log2
89 if remainder & (width - 1) != 0:
90 exc = MemException("unaligned",
91 "Unaligned access: remainder %x width %d" %
92 (remainder, width))
93 exc.dar = ldaddr
94 raise exc
95 if address in self.mem:
96 val = self.mem[address]
97 elif check_in_mem:
98 return None
99 else:
100 val = 0
101 log("ld mem @ 0x%x rem %d : 0x%x" % (ldaddr, remainder, val))
102
103 if width != self.bytes_per_word:
104 shifter, mask = self._get_shifter_mask(width, remainder)
105 log("masking", hex(val), hex(mask << shifter), shifter)
106 val = val & (mask << shifter)
107 val >>= shifter
108 if swap:
109 val = swap_order(val, width)
110 log("Read 0x%x from addr 0x%x" % (val, ldaddr))
111 return val
112
113 def _st(self, addr, v, width=8, swap=True):
114 staddr = addr
115 remainder = addr & (self.bytes_per_word - 1)
116 addr = addr >> self.word_log2
117 log("Writing 0x%x to ST 0x%x memaddr 0x%x/%x swap %s" %
118 (v, staddr, addr, remainder, str(swap)))
119 if not self.misaligned_ok and remainder & (width - 1) != 0:
120 exc = MemException("unaligned",
121 "Unaligned access: remainder %x width %d" %
122 (remainder, width))
123 exc.dar = staddr
124 raise exc
125 if swap:
126 v = swap_order(v, width)
127 if width != self.bytes_per_word:
128 if addr in self.mem:
129 val = self.mem[addr]
130 else:
131 val = 0
132 shifter, mask = self._get_shifter_mask(width, remainder)
133 val &= ~(mask << shifter)
134 val |= v << shifter
135 self.mem[addr] = val
136 else:
137 self.mem[addr] = v
138 log("mem @ 0x%x: 0x%x" % (staddr, self.mem[addr]))
139
140 def st(self, st_addr, v, width=8, swap=True):
141 self.last_st_addr = st_addr # record last store
142 # misaligned not allowed: pass straight to Mem._st
143 if not self.misaligned_ok:
144 return self._st(st_addr, v, width, swap)
145 remainder = st_addr & (self.bytes_per_word - 1)
146 if swap:
147 v = swap_order(v, width)
148 # not misaligned: pass through to Mem._st but we've swapped already
149 misaligned = remainder & (width - 1)
150 if misaligned == 0 or (remainder + width <= self.bytes_per_word):
151 return self._st(st_addr, v, width, swap=False)
152 shifter, mask = self._get_shifter_mask(width, remainder)
153 # split into two halves. lower first
154 maxmask = (1 << (self.bytes_per_word)*8) - 1
155 val1 = ((v << shifter) & maxmask) >> shifter
156 self._st(st_addr, val1, width=width-misaligned, swap=False)
157 # now upper.
158 val2 = v >> ((width-misaligned)*8)
159 addr2 = (st_addr >> self.word_log2) << self.word_log2
160 addr2 += self.bytes_per_word
161 print("v, val2", hex(v), hex(val2), "ad", addr2)
162 self._st(addr2, val2, width=width-misaligned, swap=False)
163
164 def __call__(self, addr, sz):
165 val = self.ld(addr.value, sz, swap=False)
166 log("memread", addr, sz, val)
167 return SelectableInt(val, sz*8)
168
169 def memassign(self, addr, sz, val):
170 log("memassign", addr, sz, val)
171 self.st(addr.value, val.value, sz, swap=False)
172
173 def dump(self, printout=True, asciidump=False):
174 keys = list(self.mem.keys())
175 keys.sort()
176 res = []
177 for k in keys:
178 res.append(((k*8), self.mem[k]))
179 if not printout:
180 continue
181 s = ""
182 if asciidump:
183 for i in range(8):
184 c = chr(self.mem[k] >> (i*8) & 0xff)
185 if not c.isprintable():
186 c = "."
187 s += c
188 print("%016x: %016x" % ((k*8) & 0xffffffffffffffff,
189 self.mem[k]), s)
190 return res
191
192 def log_fancy(self, *, kind=LogKind.Default, name="Memory",
193 log2_line_size=4, log2_column_chunk_size=3, log=log):
194 line_size = 1 << log2_line_size
195 subline_mask = line_size - 1
196 column_chunk_size = 1 << log2_column_chunk_size
197
198 def make_line():
199 return bytearray(line_size)
200 mem_lines = defaultdict(make_line)
201 subword_range = range(1 << self.word_log2)
202 for k in self.mem.keys():
203 addr = k << self.word_log2
204 for _ in subword_range:
205 v = self.ld(addr, width=1)
206 mem_lines[addr >> log2_line_size][addr & subline_mask] = v
207 addr += 1
208
209 lines = []
210 last_line_index = None
211 for line_index in sorted(mem_lines.keys()):
212 line_addr = line_index << log2_line_size
213 if last_line_index is not None \
214 and last_line_index + 1 != line_index:
215 lines.append("*")
216 last_line_index = line_index
217 line_bytes = mem_lines[line_index]
218 line_str = f"0x{line_addr:08X}:"
219 for col_chunk in range(0, line_size,
220 column_chunk_size):
221 line_str += " "
222 for i in range(column_chunk_size):
223 line_str += f" {line_bytes[col_chunk + i]:02X}"
224 line_str += " |"
225 for i in range(line_size):
226 if 0x20 <= line_bytes[i] <= 0x7E:
227 line_str += chr(line_bytes[i])
228 else:
229 line_str += "."
230 line_str += "|"
231 lines.append(line_str)
232 lines = "\n".join(lines)
233 log(f"\n{name}:\n{lines}\n", kind=kind)