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):
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 log("Sim-Mem", initial_mem
, self
.bytes_per_word
, self
.word_log2
)
64 for addr
, (val
, width
) in process_mem(initial_mem
, row_bytes
).items():
65 #val = swap_order(val, width)
66 self
.st(addr
, val
, width
, swap
=False)
68 def _get_shifter_mask(self
, wid
, remainder
):
69 shifter
= ((self
.bytes_per_word
- wid
) - remainder
) * \
71 # XXX https://bugs.libre-soc.org/show_bug.cgi?id=377
73 shifter
= remainder
* 8
74 mask
= (1 << (wid
* 8)) - 1
75 log("width,rem,shift,mask", wid
, remainder
, hex(shifter
), hex(mask
))
78 # TODO: Implement ld/st of lesser width
79 def ld(self
, address
, width
=8, swap
=True, check_in_mem
=False,
81 log("ld from addr 0x%x width %d" % (address
, width
),
82 swap
, check_in_mem
, instr_fetch
)
83 self
.last_ld_addr
= address
# record last load
85 remainder
= address
& (self
.bytes_per_word
- 1)
86 address
= address
>> self
.word_log2
87 if remainder
& (width
- 1) != 0:
88 exc
= MemException("unaligned", "Unaligned access Error")
91 if address
in self
.mem
:
92 val
= self
.mem
[address
]
97 log("ld mem @ 0x%x rem %d : 0x%x" % (ldaddr
, remainder
, val
))
99 if width
!= self
.bytes_per_word
:
100 shifter
, mask
= self
._get
_shifter
_mask
(width
, remainder
)
101 log("masking", hex(val
), hex(mask
<< shifter
), shifter
)
102 val
= val
& (mask
<< shifter
)
105 val
= swap_order(val
, width
)
106 log("Read 0x%x from addr 0x%x" % (val
, ldaddr
))
109 def st(self
, addr
, v
, width
=8, swap
=True):
111 self
.last_st_addr
= addr
# record last store
112 remainder
= addr
& (self
.bytes_per_word
- 1)
113 addr
= addr
>> self
.word_log2
114 log("Writing 0x%x to ST 0x%x memaddr 0x%x/%x swap %s" % \
115 (v
, staddr
, addr
, remainder
, str(swap
)))
116 if remainder
& (width
- 1) != 0:
117 exc
= MemException("unaligned", "Unaligned access Error")
121 v
= swap_order(v
, width
)
122 if width
!= self
.bytes_per_word
:
127 shifter
, mask
= self
._get
_shifter
_mask
(width
, remainder
)
128 val
&= ~
(mask
<< shifter
)
133 log("mem @ 0x%x: 0x%x" % (staddr
, self
.mem
[addr
]))
135 def __call__(self
, addr
, sz
):
136 val
= self
.ld(addr
.value
, sz
, swap
=False)
137 log("memread", addr
, sz
, val
)
138 return SelectableInt(val
, sz
*8)
140 def memassign(self
, addr
, sz
, val
):
141 log("memassign", addr
, sz
, val
)
142 self
.st(addr
.value
, val
.value
, sz
, swap
=False)
144 def dump(self
, printout
=True):
145 keys
= list(self
.mem
.keys())
149 res
.append(((k
*8), self
.mem
[k
]))
152 print ("%016x: %016x" % ((k
*8) & 0xffffffffffffffff, self
.mem
[k
]))
155 def log_fancy(self
, *, kind
=LogKind
.Default
, name
="Memory",
156 log2_line_size
=4, log2_column_chunk_size
=3):
157 line_size
= 1 << log2_line_size
158 subline_mask
= line_size
- 1
159 column_chunk_size
= 1 << log2_column_chunk_size
162 return bytearray(line_size
)
163 mem_lines
= defaultdict(make_line
)
164 subword_range
= range(1 << self
.word_log2
)
165 for k
in self
.mem
.keys():
166 addr
= k
<< self
.word_log2
167 for _
in subword_range
:
168 v
= self
.ld(addr
, width
=1)
169 mem_lines
[addr
>> log2_line_size
][addr
& subline_mask
] = v
173 last_line_index
= None
174 for line_index
in sorted(mem_lines
.keys()):
175 line_addr
= line_index
<< log2_line_size
176 if last_line_index
is not None \
177 and last_line_index
+ 1 != line_index
:
179 last_line_index
= line_index
180 line_bytes
= mem_lines
[line_index
]
181 line_str
= f
"0x{line_addr:08X}:"
182 for col_chunk
in range(0, line_size
,
185 for i
in range(column_chunk_size
):
186 line_str
+= f
" {line_bytes[col_chunk + i]:02X}"
188 for i
in range(line_size
):
189 if 0x20 <= line_bytes
[i
] <= 0x7E:
190 line_str
+= chr(line_bytes
[i
])
194 lines
.append(line_str
)
195 lines
= "\n".join(lines
)
196 log(f
"\n{name}:\n{lines}\n", kind
=kind
)