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):
30 def process_mem(initial_mem
, row_bytes
=8):
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
38 for i
, val
in enumerate(mem
):
39 initial_mem
[startaddr
+ row_bytes
*i
] = (val
, row_bytes
)
41 for addr
, val
in initial_mem
.items():
42 if isinstance(val
, tuple):
45 width
= row_bytes
# assume same width
46 #val = swap_order(val, width)
47 res
[addr
] = (val
, width
)
54 def __init__(self
, row_bytes
=8, initial_mem
=None, misaligned_ok
=False):
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
)
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)
69 def _get_shifter_mask(self
, wid
, remainder
):
70 shifter
= ((self
.bytes_per_word
- wid
) - remainder
) * \
72 # XXX https://bugs.libre-soc.org/show_bug.cgi?id=377
74 shifter
= remainder
* 8
75 mask
= (1 << (wid
* 8)) - 1
76 log("width,rem,shift,mask", wid
, remainder
, hex(shifter
), hex(mask
))
79 # TODO: Implement ld/st of lesser width
80 def ld(self
, address
, width
=8, swap
=True, check_in_mem
=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
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" % \
94 if address
in self
.mem
:
95 val
= self
.mem
[address
]
100 log("ld mem @ 0x%x rem %d : 0x%x" % (ldaddr
, remainder
, val
))
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
)
108 val
= swap_order(val
, width
)
109 log("Read 0x%x from addr 0x%x" % (val
, ldaddr
))
112 def _st(self
, addr
, v
, width
=8, swap
=True):
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" % \
125 v
= swap_order(v
, width
)
126 if width
!= self
.bytes_per_word
:
131 shifter
, mask
= self
._get
_shifter
_mask
(width
, remainder
)
132 val
&= ~
(mask
<< shifter
)
137 log("mem @ 0x%x: 0x%x" % (staddr
, self
.mem
[addr
]))
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)
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)
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)
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)
168 def memassign(self
, addr
, sz
, val
):
169 log("memassign", addr
, sz
, val
)
170 self
.st(addr
.value
, val
.value
, sz
, swap
=False)
172 def dump(self
, printout
=True, asciidump
=False):
173 keys
= list(self
.mem
.keys())
177 res
.append(((k
*8), self
.mem
[k
]))
183 c
= chr(self
.mem
[k
]>>(i
*8) & 0xff)
184 if not c
.isprintable():
187 print ("%016x: %016x" % ((k
*8) & 0xffffffffffffffff,
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
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
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
:
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
,
221 for i
in range(column_chunk_size
):
222 line_str
+= f
" {line_bytes[col_chunk + i]:02X}"
224 for i
in range(line_size
):
225 if 0x20 <= line_bytes
[i
] <= 0x7E:
226 line_str
+= chr(line_bytes
[i
])
230 lines
.append(line_str
)
231 lines
= "\n".join(lines
)
232 log(f
"\n{name}:\n{lines}\n", kind
=kind
)