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
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.
12 * https://bugs.libre-soc.org/show_bug.cgi?id=424
15 from collections
import defaultdict
16 from openpower
.decoder
.selectable_int
import SelectableInt
17 from openpower
.util
import log
, LogKind
21 def swap_order(x
, nbytes
):
22 x
= x
.to_bytes(nbytes
, byteorder
='little')
23 x
= int.from_bytes(x
, byteorder
='big', signed
=False)
27 class MemException(Exception):
31 def process_mem(initial_mem
, row_bytes
=8):
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
39 for i
, val
in enumerate(mem
):
40 initial_mem
[startaddr
+ row_bytes
*i
] = (val
, row_bytes
)
42 for addr
, val
in initial_mem
.items():
43 if isinstance(val
, tuple):
46 width
= row_bytes
# assume same width
47 # val = swap_order(val, width)
48 res
[addr
] = (val
, width
)
55 def __init__(self
, row_bytes
=8, initial_mem
=None, misaligned_ok
=False):
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
)
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)
70 def _get_shifter_mask(self
, wid
, remainder
):
71 shifter
= ((self
.bytes_per_word
- wid
) - remainder
) * \
73 # XXX https://bugs.libre-soc.org/show_bug.cgi?id=377
75 shifter
= remainder
* 8
76 mask
= (1 << (wid
* 8)) - 1
77 log("width,rem,shift,mask", wid
, remainder
, hex(shifter
), hex(mask
))
80 # TODO: Implement ld/st of lesser width
81 def ld(self
, address
, width
=8, swap
=True, check_in_mem
=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
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" %
95 if address
in self
.mem
:
96 val
= self
.mem
[address
]
101 log("ld mem @ 0x%x rem %d : 0x%x" % (ldaddr
, remainder
, val
))
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
)
109 val
= swap_order(val
, width
)
110 log("Read 0x%x from addr 0x%x" % (val
, ldaddr
))
113 def _st(self
, addr
, v
, width
=8, swap
=True):
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" %
126 v
= swap_order(v
, width
)
127 if width
!= self
.bytes_per_word
:
132 shifter
, mask
= self
._get
_shifter
_mask
(width
, remainder
)
133 val
&= ~
(mask
<< shifter
)
138 log("mem @ 0x%x: 0x%x" % (staddr
, self
.mem
[addr
]))
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)
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)
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)
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)
169 def memassign(self
, addr
, sz
, val
):
170 log("memassign", addr
, sz
, val
)
171 self
.st(addr
.value
, val
.value
, sz
, swap
=False)
173 def dump(self
, printout
=True, asciidump
=False):
174 keys
= list(self
.mem
.keys())
178 res
.append(((k
*8), self
.mem
[k
]))
184 c
= chr(self
.mem
[k
] >> (i
*8) & 0xff)
185 if not c
.isprintable():
188 print("%016x: %016x" % ((k
*8) & 0xffffffffffffffff,
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
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
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
:
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
,
222 for i
in range(column_chunk_size
):
223 line_str
+= f
" {line_bytes[col_chunk + i]:02X}"
225 for i
in range(line_size
):
226 if 0x20 <= line_bytes
[i
] <= 0x7E:
227 line_str
+= chr(line_bytes
[i
])
231 lines
.append(line_str
)
232 lines
= "\n".join(lines
)
233 log(f
"\n{name}:\n{lines}\n", kind
=kind
)