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