QuizGenerator 0.4.2__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- QuizGenerator/contentast.py +809 -117
- QuizGenerator/generate.py +219 -11
- QuizGenerator/misc.py +0 -556
- QuizGenerator/mixins.py +50 -29
- QuizGenerator/premade_questions/basic.py +3 -3
- QuizGenerator/premade_questions/cst334/languages.py +183 -175
- QuizGenerator/premade_questions/cst334/math_questions.py +81 -70
- QuizGenerator/premade_questions/cst334/memory_questions.py +262 -165
- QuizGenerator/premade_questions/cst334/persistence_questions.py +83 -60
- QuizGenerator/premade_questions/cst334/process.py +558 -79
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +39 -13
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +61 -36
- QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +29 -10
- QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +2 -2
- QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +60 -43
- QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +173 -326
- QuizGenerator/premade_questions/cst463/models/attention.py +29 -14
- QuizGenerator/premade_questions/cst463/models/cnns.py +32 -20
- QuizGenerator/premade_questions/cst463/models/rnns.py +28 -15
- QuizGenerator/premade_questions/cst463/models/text.py +29 -15
- QuizGenerator/premade_questions/cst463/models/weight_counting.py +38 -30
- QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +91 -111
- QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +128 -55
- QuizGenerator/question.py +114 -20
- QuizGenerator/quiz.py +81 -24
- QuizGenerator/regenerate.py +98 -29
- {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/METADATA +1 -1
- {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/RECORD +31 -33
- QuizGenerator/README.md +0 -5
- QuizGenerator/logging.yaml +0 -55
- {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/WHEEL +0 -0
- {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/entry_points.txt +0 -0
- {quizgenerator-0.4.2.dist-info → quizgenerator-0.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,8 +9,8 @@ import logging
|
|
|
9
9
|
import math
|
|
10
10
|
from typing import List, Optional
|
|
11
11
|
|
|
12
|
-
from QuizGenerator.contentast import ContentAST
|
|
13
|
-
from QuizGenerator.question import Question,
|
|
12
|
+
from QuizGenerator.contentast import ContentAST, AnswerTypes
|
|
13
|
+
from QuizGenerator.question import Question, QuestionRegistry, RegenerableChoiceMixin
|
|
14
14
|
from QuizGenerator.mixins import TableQuestionMixin, BodyTemplatesMixin
|
|
15
15
|
|
|
16
16
|
log = logging.getLogger(__name__)
|
|
@@ -40,9 +40,9 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
40
40
|
self.num_bits_vpn = self.num_bits_va - self.num_bits_offset
|
|
41
41
|
|
|
42
42
|
self.possible_answers = {
|
|
43
|
-
self.Target.VA_BITS:
|
|
44
|
-
self.Target.OFFSET_BITS:
|
|
45
|
-
self.Target.VPN_BITS:
|
|
43
|
+
self.Target.VA_BITS: AnswerTypes.Int(self.num_bits_va, unit="bits"),
|
|
44
|
+
self.Target.OFFSET_BITS: AnswerTypes.Int(self.num_bits_offset, unit="bits"),
|
|
45
|
+
self.Target.VPN_BITS: AnswerTypes.Int(self.num_bits_vpn, unit="bits")
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
# Select what kind of question we are going to be
|
|
@@ -52,22 +52,25 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
52
52
|
|
|
53
53
|
return
|
|
54
54
|
|
|
55
|
-
def
|
|
55
|
+
def _get_body(self, **kwargs):
|
|
56
|
+
"""Build question body and collect answers."""
|
|
57
|
+
answers = [self.answers['answer']] # Collect the answer
|
|
58
|
+
|
|
56
59
|
# Create table data with one blank cell
|
|
57
60
|
table_data = [{}]
|
|
58
61
|
for target in list(self.Target):
|
|
59
62
|
if target == self.blank_kind:
|
|
60
63
|
# This cell should be an answer blank
|
|
61
|
-
table_data[0][target.value] =
|
|
64
|
+
table_data[0][target.value] = self.possible_answers[target]
|
|
62
65
|
else:
|
|
63
66
|
# This cell shows the value
|
|
64
67
|
table_data[0][target.value] = f"{self.possible_answers[target].display} bits"
|
|
65
|
-
|
|
68
|
+
|
|
66
69
|
table = self.create_fill_in_table(
|
|
67
70
|
headers=[t.value for t in list(self.Target)],
|
|
68
71
|
template_rows=table_data
|
|
69
72
|
)
|
|
70
|
-
|
|
73
|
+
|
|
71
74
|
body = ContentAST.Section()
|
|
72
75
|
body.add_element(
|
|
73
76
|
ContentAST.Paragraph(
|
|
@@ -77,11 +80,17 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
77
80
|
)
|
|
78
81
|
)
|
|
79
82
|
body.add_element(table)
|
|
83
|
+
return body, answers
|
|
84
|
+
|
|
85
|
+
def get_body(self, **kwargs) -> ContentAST.Section:
|
|
86
|
+
"""Build question body (backward compatible interface)."""
|
|
87
|
+
body, _ = self._get_body(**kwargs)
|
|
80
88
|
return body
|
|
81
|
-
|
|
82
|
-
def
|
|
89
|
+
|
|
90
|
+
def _get_explanation(self, **kwargs):
|
|
91
|
+
"""Build question explanation."""
|
|
83
92
|
explanation = ContentAST.Section()
|
|
84
|
-
|
|
93
|
+
|
|
85
94
|
explanation.add_element(
|
|
86
95
|
ContentAST.Paragraph(
|
|
87
96
|
[
|
|
@@ -92,7 +101,7 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
92
101
|
]
|
|
93
102
|
)
|
|
94
103
|
)
|
|
95
|
-
|
|
104
|
+
|
|
96
105
|
explanation.add_element(
|
|
97
106
|
ContentAST.Paragraph(
|
|
98
107
|
[
|
|
@@ -104,7 +113,12 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
104
113
|
]
|
|
105
114
|
)
|
|
106
115
|
)
|
|
107
|
-
|
|
116
|
+
|
|
117
|
+
return explanation, []
|
|
118
|
+
|
|
119
|
+
def get_explanation(self, **kwargs) -> ContentAST.Section:
|
|
120
|
+
"""Build question explanation (backward compatible interface)."""
|
|
121
|
+
explanation, _ = self._get_explanation(**kwargs)
|
|
108
122
|
return explanation
|
|
109
123
|
|
|
110
124
|
|
|
@@ -219,6 +233,7 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
219
233
|
number_of_hits = 0
|
|
220
234
|
for (request_number, request) in enumerate(self.requests):
|
|
221
235
|
was_hit, evicted, cache_state = self.cache.query_cache(request, request_number)
|
|
236
|
+
log.debug(f"cache_state: \"{cache_state}\"")
|
|
222
237
|
if was_hit:
|
|
223
238
|
number_of_hits += 1
|
|
224
239
|
self.request_results[request_number] = {
|
|
@@ -230,29 +245,29 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
230
245
|
|
|
231
246
|
self.answers.update(
|
|
232
247
|
{
|
|
233
|
-
f"answer__hit-{request_number}":
|
|
234
|
-
|
|
235
|
-
),
|
|
236
|
-
f"answer__evicted-{request_number}": Answer.string(
|
|
237
|
-
f"answer__evicted-{request_number}", ('-' if evicted is None else f"{evicted}")
|
|
238
|
-
),
|
|
239
|
-
f"answer__cache_state-{request_number}": Answer.list_value(
|
|
240
|
-
f"answer__cache_state-{request_number}", copy.copy(cache_state)
|
|
241
|
-
),
|
|
248
|
+
f"answer__hit-{request_number}": AnswerTypes.String(('hit' if was_hit else 'miss')),
|
|
249
|
+
f"answer__evicted-{request_number}": AnswerTypes.String(('-' if evicted is None else f"{evicted}")),
|
|
250
|
+
f"answer__cache_state-{request_number}": AnswerTypes.List(value=copy.copy(cache_state), order_matters=True),
|
|
242
251
|
}
|
|
243
252
|
)
|
|
244
253
|
|
|
245
254
|
self.hit_rate = 100 * number_of_hits / (self.num_requests)
|
|
246
255
|
self.answers.update(
|
|
247
256
|
{
|
|
248
|
-
"answer__hit_rate":
|
|
257
|
+
"answer__hit_rate": AnswerTypes.Float(self.hit_rate,
|
|
258
|
+
label=f"Hit rate, excluding non-capacity misses",
|
|
259
|
+
unit="%"
|
|
260
|
+
)
|
|
249
261
|
}
|
|
250
262
|
)
|
|
251
263
|
|
|
252
264
|
# Return whether this workload is interesting
|
|
253
265
|
return self.is_interesting()
|
|
254
266
|
|
|
255
|
-
def
|
|
267
|
+
def _get_body(self, **kwargs):
|
|
268
|
+
"""Build question body and collect answers."""
|
|
269
|
+
answers = []
|
|
270
|
+
|
|
256
271
|
# Create table data for cache simulation
|
|
257
272
|
table_rows = []
|
|
258
273
|
for request_number in sorted(self.request_results.keys()):
|
|
@@ -264,47 +279,55 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
264
279
|
"Cache State": f"answer__cache_state-{request_number}" # Answer key
|
|
265
280
|
}
|
|
266
281
|
)
|
|
267
|
-
|
|
282
|
+
# Collect answers for this request
|
|
283
|
+
answers.append(self.answers[f"answer__hit-{request_number}"])
|
|
284
|
+
answers.append(self.answers[f"answer__evicted-{request_number}"])
|
|
285
|
+
answers.append(self.answers[f"answer__cache_state-{request_number}"])
|
|
286
|
+
|
|
268
287
|
# Create table using mixin - automatically handles answer conversion
|
|
269
288
|
cache_table = self.create_answer_table(
|
|
270
289
|
headers=["Page Requested", "Hit/Miss", "Evicted", "Cache State"],
|
|
271
290
|
data_rows=table_rows,
|
|
272
291
|
answer_columns=["Hit/Miss", "Evicted", "Cache State"]
|
|
273
292
|
)
|
|
274
|
-
|
|
293
|
+
|
|
294
|
+
# Collect hit rate answer
|
|
295
|
+
hit_rate_answer = self.answers["answer__hit_rate"]
|
|
296
|
+
answers.append(hit_rate_answer)
|
|
297
|
+
|
|
275
298
|
# Create hit rate answer block
|
|
276
|
-
hit_rate_block = ContentAST.AnswerBlock(
|
|
277
|
-
|
|
278
|
-
answer=self.answers["answer__hit_rate"],
|
|
279
|
-
label=f"Hit rate, excluding capacity misses. If appropriate, round to {Answer.DEFAULT_ROUNDING_DIGITS} decimal digits.",
|
|
280
|
-
unit="%"
|
|
281
|
-
)
|
|
282
|
-
)
|
|
283
|
-
|
|
299
|
+
hit_rate_block = ContentAST.AnswerBlock(hit_rate_answer)
|
|
300
|
+
|
|
284
301
|
# Use mixin to create complete body
|
|
285
302
|
intro_text = (
|
|
286
|
-
f"Assume we are using a
|
|
303
|
+
f"Assume we are using a **{self.cache_policy}** caching policy and a cache size of **{self.cache_size}**. "
|
|
287
304
|
"Given the below series of requests please fill in the table. "
|
|
288
305
|
"For the hit/miss column, please write either \"hit\" or \"miss\". "
|
|
289
306
|
"For the eviction column, please write either the number of the evicted page or simply a dash (e.g. \"-\")."
|
|
290
307
|
)
|
|
291
|
-
|
|
308
|
+
|
|
292
309
|
instructions = ContentAST.OnlyHtml([
|
|
293
310
|
"For the cache state, please enter the cache contents in the order suggested in class, "
|
|
294
311
|
"which means separated by commas with spaces (e.g. \"1, 2, 3\") "
|
|
295
312
|
"and with the left-most being the next to be evicted. "
|
|
296
313
|
"In the case where there is a tie, order by increasing number."
|
|
297
314
|
])
|
|
298
|
-
|
|
315
|
+
|
|
299
316
|
body = self.create_fill_in_table_body(intro_text, instructions, cache_table)
|
|
300
317
|
body.add_element(hit_rate_block)
|
|
318
|
+
return body, answers
|
|
319
|
+
|
|
320
|
+
def get_body(self, **kwargs) -> ContentAST.Section:
|
|
321
|
+
"""Build question body (backward compatible interface)."""
|
|
322
|
+
body, _ = self._get_body(**kwargs)
|
|
301
323
|
return body
|
|
302
|
-
|
|
303
|
-
def
|
|
324
|
+
|
|
325
|
+
def _get_explanation(self, **kwargs):
|
|
326
|
+
"""Build question explanation."""
|
|
304
327
|
explanation = ContentAST.Section()
|
|
305
|
-
|
|
328
|
+
|
|
306
329
|
explanation.add_element(ContentAST.Paragraph(["The full caching table can be seen below."]))
|
|
307
|
-
|
|
330
|
+
|
|
308
331
|
explanation.add_element(
|
|
309
332
|
ContentAST.Table(
|
|
310
333
|
headers=["Page", "Hit/Miss", "Evicted", "Cache State"],
|
|
@@ -319,7 +342,7 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
319
342
|
]
|
|
320
343
|
)
|
|
321
344
|
)
|
|
322
|
-
|
|
345
|
+
|
|
323
346
|
explanation.add_element(
|
|
324
347
|
ContentAST.Paragraph(
|
|
325
348
|
[
|
|
@@ -330,7 +353,12 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
330
353
|
]
|
|
331
354
|
)
|
|
332
355
|
)
|
|
333
|
-
|
|
356
|
+
|
|
357
|
+
return explanation, []
|
|
358
|
+
|
|
359
|
+
def get_explanation(self, **kwargs) -> ContentAST.Section:
|
|
360
|
+
"""Build question explanation (backward compatible interface)."""
|
|
361
|
+
explanation, _ = self._get_explanation(**kwargs)
|
|
334
362
|
return explanation
|
|
335
363
|
|
|
336
364
|
def is_interesting(self) -> bool:
|
|
@@ -365,30 +393,32 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
365
393
|
self.virtual_address = self.rng.randint(1, int(self.bounds / self.PROBABILITY_OF_VALID))
|
|
366
394
|
|
|
367
395
|
if self.virtual_address < self.bounds:
|
|
368
|
-
self.answers["answer"] =
|
|
369
|
-
"answer__physical_address",
|
|
396
|
+
self.answers["answer"] = AnswerTypes.Hex(
|
|
370
397
|
self.base + self.virtual_address,
|
|
371
398
|
length=math.ceil(math.log2(self.base + self.virtual_address))
|
|
372
399
|
)
|
|
373
400
|
else:
|
|
374
|
-
self.answers["answer"] =
|
|
401
|
+
self.answers["answer"] = AnswerTypes.String("INVALID")
|
|
375
402
|
|
|
376
|
-
def
|
|
403
|
+
def _get_body(self):
|
|
404
|
+
"""Build question body and collect answers."""
|
|
405
|
+
answers = [self.answers["answer"]]
|
|
406
|
+
|
|
377
407
|
# Use mixin to create parameter table with answer
|
|
378
408
|
parameter_info = {
|
|
379
409
|
"Base": f"0x{self.base:X}",
|
|
380
410
|
"Bounds": f"0x{self.bounds:X}",
|
|
381
411
|
"Virtual Address": f"0x{self.virtual_address:X}"
|
|
382
412
|
}
|
|
383
|
-
|
|
413
|
+
|
|
384
414
|
table = self.create_parameter_answer_table(
|
|
385
415
|
parameter_info=parameter_info,
|
|
386
416
|
answer_label="Physical Address",
|
|
387
417
|
answer_key="answer",
|
|
388
418
|
transpose=True
|
|
389
419
|
)
|
|
390
|
-
|
|
391
|
-
|
|
420
|
+
|
|
421
|
+
body = self.create_parameter_calculation_body(
|
|
392
422
|
intro_text=(
|
|
393
423
|
"Given the information in the below table, "
|
|
394
424
|
"please calcuate the physical address associated with the given virtual address. "
|
|
@@ -396,10 +426,17 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
396
426
|
),
|
|
397
427
|
parameter_table=table
|
|
398
428
|
)
|
|
399
|
-
|
|
400
|
-
|
|
429
|
+
return body, answers
|
|
430
|
+
|
|
431
|
+
def get_body(self) -> ContentAST.Section:
|
|
432
|
+
"""Build question body (backward compatible interface)."""
|
|
433
|
+
body, _ = self._get_body()
|
|
434
|
+
return body
|
|
435
|
+
|
|
436
|
+
def _get_explanation(self):
|
|
437
|
+
"""Build question explanation."""
|
|
401
438
|
explanation = ContentAST.Section()
|
|
402
|
-
|
|
439
|
+
|
|
403
440
|
explanation.add_element(
|
|
404
441
|
ContentAST.Paragraph(
|
|
405
442
|
[
|
|
@@ -410,7 +447,7 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
410
447
|
]
|
|
411
448
|
)
|
|
412
449
|
)
|
|
413
|
-
|
|
450
|
+
|
|
414
451
|
explanation.add_element(
|
|
415
452
|
ContentAST.Paragraph(
|
|
416
453
|
[
|
|
@@ -419,7 +456,7 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
419
456
|
]
|
|
420
457
|
)
|
|
421
458
|
)
|
|
422
|
-
|
|
459
|
+
|
|
423
460
|
if self.virtual_address < self.bounds:
|
|
424
461
|
explanation.add_element(
|
|
425
462
|
ContentAST.Paragraph(
|
|
@@ -442,7 +479,12 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
442
479
|
]
|
|
443
480
|
)
|
|
444
481
|
)
|
|
445
|
-
|
|
482
|
+
|
|
483
|
+
return explanation, []
|
|
484
|
+
|
|
485
|
+
def get_explanation(self) -> ContentAST.Section:
|
|
486
|
+
"""Build question explanation (backward compatible interface)."""
|
|
487
|
+
explanation, _ = self._get_explanation()
|
|
446
488
|
return explanation
|
|
447
489
|
|
|
448
490
|
|
|
@@ -537,22 +579,25 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
537
579
|
|
|
538
580
|
# Set answers based on whether it's in bounds or not
|
|
539
581
|
if self.__within_bounds(self.segment, self.offset, self.bounds[self.segment]):
|
|
540
|
-
self.answers["answer__physical_address"] =
|
|
541
|
-
"answer__physical_address",
|
|
582
|
+
self.answers["answer__physical_address"] = AnswerTypes.Binary(
|
|
542
583
|
self.physical_address,
|
|
543
|
-
length=self.physical_bits
|
|
584
|
+
length=self.physical_bits,
|
|
585
|
+
label="Physical Address"
|
|
544
586
|
)
|
|
545
587
|
else:
|
|
546
|
-
self.answers["answer__physical_address"] =
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
)
|
|
550
|
-
|
|
551
|
-
self.answers["answer__segment"] = Answer.string("answer__segment", self.segment)
|
|
588
|
+
self.answers["answer__physical_address"] = AnswerTypes.String("INVALID", label="Physical Address")
|
|
589
|
+
|
|
590
|
+
self.answers["answer__segment"] = AnswerTypes.String(self.segment, label="Segment name")
|
|
552
591
|
|
|
553
|
-
def
|
|
592
|
+
def _get_body(self):
|
|
593
|
+
"""Build question body and collect answers."""
|
|
594
|
+
answers = [
|
|
595
|
+
self.answers["answer__segment"],
|
|
596
|
+
self.answers["answer__physical_address"]
|
|
597
|
+
]
|
|
598
|
+
|
|
554
599
|
body = ContentAST.Section()
|
|
555
|
-
|
|
600
|
+
|
|
556
601
|
body.add_element(
|
|
557
602
|
ContentAST.Paragraph(
|
|
558
603
|
[
|
|
@@ -565,39 +610,36 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
565
610
|
]
|
|
566
611
|
)
|
|
567
612
|
)
|
|
568
|
-
|
|
613
|
+
|
|
569
614
|
# Create segment table using mixin
|
|
570
615
|
segment_rows = [
|
|
571
616
|
{"": "code", "base": f"0b{self.base['code']:0{self.physical_bits}b}", "bounds": f"0b{self.bounds['code']:0b}"},
|
|
572
617
|
{"": "heap", "base": f"0b{self.base['heap']:0{self.physical_bits}b}", "bounds": f"0b{self.bounds['heap']:0b}"},
|
|
573
618
|
{"": "stack", "base": f"0b{self.base['stack']:0{self.physical_bits}b}", "bounds": f"0b{self.bounds['stack']:0b}"}
|
|
574
619
|
]
|
|
575
|
-
|
|
620
|
+
|
|
576
621
|
segment_table = self.create_answer_table(
|
|
577
622
|
headers=["", "base", "bounds"],
|
|
578
623
|
data_rows=segment_rows,
|
|
579
624
|
answer_columns=[] # No answer columns in this table
|
|
580
625
|
)
|
|
581
|
-
|
|
626
|
+
|
|
582
627
|
body.add_element(segment_table)
|
|
583
|
-
|
|
628
|
+
|
|
584
629
|
body.add_element(
|
|
585
|
-
ContentAST.AnswerBlock(
|
|
586
|
-
[
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
label="Segment name"
|
|
590
|
-
),
|
|
591
|
-
ContentAST.Answer(
|
|
592
|
-
self.answers["answer__physical_address"],
|
|
593
|
-
label="Physical Address"
|
|
594
|
-
)
|
|
595
|
-
]
|
|
596
|
-
)
|
|
630
|
+
ContentAST.AnswerBlock([
|
|
631
|
+
self.answers["answer__segment"],
|
|
632
|
+
self.answers["answer__physical_address"]
|
|
633
|
+
])
|
|
597
634
|
)
|
|
635
|
+
return body, answers
|
|
636
|
+
|
|
637
|
+
def get_body(self) -> ContentAST.Section:
|
|
638
|
+
"""Build question body (backward compatible interface)."""
|
|
639
|
+
body, _ = self._get_body()
|
|
598
640
|
return body
|
|
599
|
-
|
|
600
|
-
def
|
|
641
|
+
|
|
642
|
+
def _get_explanation(self):
|
|
601
643
|
explanation = ContentAST.Section()
|
|
602
644
|
|
|
603
645
|
explanation.add_element(
|
|
@@ -691,7 +733,12 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
691
733
|
f" 0b{self.physical_address:0{self.physical_bits}b}\n"
|
|
692
734
|
)
|
|
693
735
|
)
|
|
694
|
-
|
|
736
|
+
|
|
737
|
+
return explanation, []
|
|
738
|
+
|
|
739
|
+
def get_explanation(self) -> ContentAST.Section:
|
|
740
|
+
"""Build question explanation (backward compatible interface)."""
|
|
741
|
+
explanation, _ = self._get_explanation()
|
|
695
742
|
return explanation
|
|
696
743
|
|
|
697
744
|
|
|
@@ -765,34 +812,43 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
765
812
|
|
|
766
813
|
self.answers.update(
|
|
767
814
|
{
|
|
768
|
-
"answer__vpn":
|
|
769
|
-
"answer__offset":
|
|
770
|
-
"answer__pte":
|
|
815
|
+
"answer__vpn": AnswerTypes.Binary(self.vpn, length=self.num_bits_vpn, label="VPN"),
|
|
816
|
+
"answer__offset": AnswerTypes.Binary(self.offset, length=self.num_bits_offset, label="Offset"),
|
|
817
|
+
"answer__pte": AnswerTypes.Binary(self.pte, length=(self.num_bits_pfn + 1), label="PTE"),
|
|
771
818
|
}
|
|
772
819
|
)
|
|
773
820
|
|
|
774
821
|
if self.is_valid:
|
|
775
822
|
self.answers.update(
|
|
776
823
|
{
|
|
777
|
-
"answer__is_valid":
|
|
778
|
-
"answer__pfn":
|
|
779
|
-
"answer__physical_address":
|
|
780
|
-
"answer__physical_address", self.physical_address, length=(self.num_bits_pfn + self.num_bits_offset)
|
|
824
|
+
"answer__is_valid": AnswerTypes.String("VALID", label="VALID or INVALID?"),
|
|
825
|
+
"answer__pfn": AnswerTypes.Binary(self.pfn, length=self.num_bits_pfn, label="PFN"),
|
|
826
|
+
"answer__physical_address": AnswerTypes.Binary(self.physical_address, length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
781
827
|
),
|
|
782
828
|
}
|
|
783
829
|
)
|
|
784
830
|
else:
|
|
785
831
|
self.answers.update(
|
|
786
832
|
{
|
|
787
|
-
"answer__is_valid":
|
|
788
|
-
"answer__pfn":
|
|
789
|
-
"answer__physical_address":
|
|
833
|
+
"answer__is_valid": AnswerTypes.String("INVALID", label="VALID or INVALID?"),
|
|
834
|
+
"answer__pfn": AnswerTypes.String("INVALID", label="PFN"),
|
|
835
|
+
"answer__physical_address": AnswerTypes.String("INVALID", label="Physical Address"),
|
|
790
836
|
}
|
|
791
837
|
)
|
|
792
838
|
|
|
793
|
-
def
|
|
839
|
+
def _get_body(self, *args, **kwargs):
|
|
840
|
+
"""Build question body and collect answers."""
|
|
841
|
+
answers = [
|
|
842
|
+
self.answers["answer__vpn"],
|
|
843
|
+
self.answers["answer__offset"],
|
|
844
|
+
self.answers["answer__pte"],
|
|
845
|
+
self.answers["answer__is_valid"],
|
|
846
|
+
self.answers["answer__pfn"],
|
|
847
|
+
self.answers["answer__physical_address"],
|
|
848
|
+
]
|
|
849
|
+
|
|
794
850
|
body = ContentAST.Section()
|
|
795
|
-
|
|
851
|
+
|
|
796
852
|
body.add_element(
|
|
797
853
|
ContentAST.Paragraph(
|
|
798
854
|
[
|
|
@@ -801,14 +857,14 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
801
857
|
]
|
|
802
858
|
)
|
|
803
859
|
)
|
|
804
|
-
|
|
860
|
+
|
|
805
861
|
# Create parameter info table using mixin
|
|
806
862
|
parameter_info = {
|
|
807
863
|
"Virtual Address": f"0b{self.virtual_address:0{self.num_bits_vpn + self.num_bits_offset}b}",
|
|
808
864
|
"# VPN bits": f"{self.num_bits_vpn}",
|
|
809
865
|
"# PFN bits": f"{self.num_bits_pfn}"
|
|
810
866
|
}
|
|
811
|
-
|
|
867
|
+
|
|
812
868
|
body.add_element(self.create_info_table(parameter_info))
|
|
813
869
|
|
|
814
870
|
# Use the page table generated in refresh() for deterministic output
|
|
@@ -827,33 +883,36 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
827
883
|
|
|
828
884
|
if (max(self.page_table.keys()) + 1) != 2 ** self.num_bits_vpn:
|
|
829
885
|
value_matrix.append(["...", "..."])
|
|
830
|
-
|
|
886
|
+
|
|
831
887
|
body.add_element(
|
|
832
888
|
ContentAST.Table(
|
|
833
889
|
headers=["VPN", "PTE"],
|
|
834
890
|
data=value_matrix
|
|
835
891
|
)
|
|
836
892
|
)
|
|
837
|
-
|
|
893
|
+
|
|
838
894
|
body.add_element(
|
|
839
|
-
ContentAST.AnswerBlock(
|
|
840
|
-
[
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
ContentAST.Answer(self.answers["answer__physical_address"], label="Physical Address"),
|
|
848
|
-
]
|
|
849
|
-
)
|
|
895
|
+
ContentAST.AnswerBlock([
|
|
896
|
+
self.answers["answer__vpn"],
|
|
897
|
+
self.answers["answer__offset"],
|
|
898
|
+
self.answers["answer__pte"],
|
|
899
|
+
self.answers["answer__is_valid"],
|
|
900
|
+
self.answers["answer__pfn"],
|
|
901
|
+
self.answers["answer__physical_address"],
|
|
902
|
+
])
|
|
850
903
|
)
|
|
851
|
-
|
|
904
|
+
|
|
905
|
+
return body, answers
|
|
906
|
+
|
|
907
|
+
def get_body(self, *args, **kwargs) -> ContentAST.Section:
|
|
908
|
+
"""Build question body (backward compatible interface)."""
|
|
909
|
+
body, _ = self._get_body(*args, **kwargs)
|
|
852
910
|
return body
|
|
853
911
|
|
|
854
|
-
def
|
|
912
|
+
def _get_explanation(self, *args, **kwargs):
|
|
913
|
+
"""Build question explanation."""
|
|
855
914
|
explanation = ContentAST.Section()
|
|
856
|
-
|
|
915
|
+
|
|
857
916
|
explanation.add_element(
|
|
858
917
|
ContentAST.Paragraph(
|
|
859
918
|
[
|
|
@@ -863,7 +922,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
863
922
|
]
|
|
864
923
|
)
|
|
865
924
|
)
|
|
866
|
-
|
|
925
|
+
|
|
867
926
|
explanation.add_element(
|
|
868
927
|
ContentAST.Paragraph(
|
|
869
928
|
[
|
|
@@ -871,7 +930,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
871
930
|
]
|
|
872
931
|
)
|
|
873
932
|
)
|
|
874
|
-
|
|
933
|
+
|
|
875
934
|
explanation.add_element(
|
|
876
935
|
ContentAST.Paragraph(
|
|
877
936
|
[
|
|
@@ -881,7 +940,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
881
940
|
]
|
|
882
941
|
)
|
|
883
942
|
)
|
|
884
|
-
|
|
943
|
+
|
|
885
944
|
explanation.add_element(
|
|
886
945
|
ContentAST.Paragraph(
|
|
887
946
|
[
|
|
@@ -892,12 +951,12 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
892
951
|
]
|
|
893
952
|
)
|
|
894
953
|
)
|
|
895
|
-
|
|
954
|
+
|
|
896
955
|
if self.is_valid:
|
|
897
956
|
explanation.add_element(
|
|
898
957
|
ContentAST.Paragraph(
|
|
899
958
|
[
|
|
900
|
-
f"In our PTE we see that the first bit is
|
|
959
|
+
f"In our PTE we see that the first bit is **{self.pte // (2 ** self.num_bits_pfn)}** meaning that the translation is **VALID**"
|
|
901
960
|
]
|
|
902
961
|
)
|
|
903
962
|
)
|
|
@@ -905,14 +964,14 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
905
964
|
explanation.add_element(
|
|
906
965
|
ContentAST.Paragraph(
|
|
907
966
|
[
|
|
908
|
-
f"In our PTE we see that the first bit is
|
|
967
|
+
f"In our PTE we see that the first bit is **{self.pte // (2 ** self.num_bits_pfn)}** meaning that the translation is **INVALID**.",
|
|
909
968
|
"Therefore, we just write \"INVALID\" as our answer.",
|
|
910
969
|
"If it were valid we would complete the below steps.",
|
|
911
970
|
"<hr>"
|
|
912
971
|
]
|
|
913
972
|
)
|
|
914
973
|
)
|
|
915
|
-
|
|
974
|
+
|
|
916
975
|
explanation.add_element(
|
|
917
976
|
ContentAST.Paragraph(
|
|
918
977
|
[
|
|
@@ -930,7 +989,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
930
989
|
f"\\& \\texttt{{0b{(2 ** self.num_bits_pfn) - 1:0{self.num_bits_pfn + 1}b}}}"
|
|
931
990
|
)
|
|
932
991
|
)
|
|
933
|
-
|
|
992
|
+
|
|
934
993
|
explanation.add_elements(
|
|
935
994
|
[
|
|
936
995
|
ContentAST.Paragraph(
|
|
@@ -944,7 +1003,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
944
1003
|
)
|
|
945
1004
|
]
|
|
946
1005
|
)
|
|
947
|
-
|
|
1006
|
+
|
|
948
1007
|
explanation.add_elements(
|
|
949
1008
|
[
|
|
950
1009
|
ContentAST.Paragraph(["Note: Strictly speaking, this calculation is:", ]),
|
|
@@ -955,6 +1014,11 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
955
1014
|
]
|
|
956
1015
|
)
|
|
957
1016
|
|
|
1017
|
+
return explanation, []
|
|
1018
|
+
|
|
1019
|
+
def get_explanation(self, *args, **kwargs) -> ContentAST.Section:
|
|
1020
|
+
"""Build question explanation (backward compatible interface)."""
|
|
1021
|
+
explanation, _ = self._get_explanation(*args, **kwargs)
|
|
958
1022
|
return explanation
|
|
959
1023
|
|
|
960
1024
|
|
|
@@ -1108,49 +1172,71 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1108
1172
|
|
|
1109
1173
|
# Set up answers
|
|
1110
1174
|
self.answers.update({
|
|
1111
|
-
"answer__pdi":
|
|
1112
|
-
|
|
1113
|
-
"
|
|
1114
|
-
|
|
1115
|
-
"
|
|
1175
|
+
"answer__pdi": AnswerTypes.Binary(self.pdi, length=self.num_bits_pdi,
|
|
1176
|
+
label="PDI (Page Directory Index)"),
|
|
1177
|
+
"answer__pti": AnswerTypes.Binary(self.pti, length=self.num_bits_pti,
|
|
1178
|
+
label="PTI (Page Table Index)"),
|
|
1179
|
+
"answer__offset": AnswerTypes.Binary(self.offset, length=self.num_bits_offset,
|
|
1180
|
+
label="Offset"),
|
|
1181
|
+
"answer__pd_entry": AnswerTypes.Binary(self.pd_entry, length=(self.num_bits_pfn + 1),
|
|
1182
|
+
label="PD Entry (from Page Directory)"),
|
|
1183
|
+
"answer__pt_number": (
|
|
1184
|
+
AnswerTypes.Binary(self.page_table_number, length=self.num_bits_pfn,
|
|
1185
|
+
label="Page Table Number")
|
|
1186
|
+
if self.pd_valid
|
|
1187
|
+
else AnswerTypes.String("INVALID", label="Page Table Number")
|
|
1188
|
+
),
|
|
1116
1189
|
})
|
|
1117
1190
|
|
|
1118
1191
|
# PTE answer: if PD is valid, accept the actual PTE value from the table
|
|
1119
1192
|
# (regardless of whether that PTE is valid or invalid)
|
|
1120
1193
|
if self.pd_valid:
|
|
1121
1194
|
self.answers.update({
|
|
1122
|
-
"answer__pte":
|
|
1195
|
+
"answer__pte": AnswerTypes.Binary(self.pte, length=(self.num_bits_pfn + 1),
|
|
1196
|
+
label="PTE (from Page Table)"),
|
|
1123
1197
|
})
|
|
1124
1198
|
else:
|
|
1125
1199
|
# If PD is invalid, student can't look up the page table
|
|
1126
1200
|
# Accept both "INVALID" (for consistency) and "N/A" (for accuracy)
|
|
1127
1201
|
self.answers.update({
|
|
1128
|
-
"answer__pte":
|
|
1202
|
+
"answer__pte": AnswerTypes.String(["INVALID", "N/A"], label="PTE (from Page Table)"),
|
|
1129
1203
|
})
|
|
1130
1204
|
|
|
1131
1205
|
# Validity, PFN, and Physical Address depend on BOTH levels being valid
|
|
1132
1206
|
if self.pd_valid and self.pt_valid:
|
|
1133
1207
|
self.answers.update({
|
|
1134
|
-
"answer__is_valid":
|
|
1135
|
-
"answer__pfn":
|
|
1136
|
-
"answer__physical_address":
|
|
1137
|
-
"answer__physical_address", self.physical_address, length=(self.num_bits_pfn + self.num_bits_offset)
|
|
1208
|
+
"answer__is_valid": AnswerTypes.String("VALID", label="VALID or INVALID?"),
|
|
1209
|
+
"answer__pfn": AnswerTypes.Binary(self.pfn, length=self.num_bits_pfn, label="PFN"),
|
|
1210
|
+
"answer__physical_address": AnswerTypes.Binary(self.physical_address, length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
1138
1211
|
),
|
|
1139
1212
|
})
|
|
1140
1213
|
else:
|
|
1141
1214
|
self.answers.update({
|
|
1142
|
-
"answer__is_valid":
|
|
1143
|
-
"answer__pfn":
|
|
1144
|
-
"answer__physical_address":
|
|
1215
|
+
"answer__is_valid": AnswerTypes.String("INVALID", label="VALID or INVALID?"),
|
|
1216
|
+
"answer__pfn": AnswerTypes.String("INVALID", label="PFN"),
|
|
1217
|
+
"answer__physical_address": AnswerTypes.String("INVALID", label="Physical Address"),
|
|
1145
1218
|
})
|
|
1146
1219
|
|
|
1147
|
-
def
|
|
1220
|
+
def _get_body(self, *args, **kwargs):
|
|
1221
|
+
"""Build question body and collect answers."""
|
|
1222
|
+
answers = [
|
|
1223
|
+
self.answers["answer__pdi"],
|
|
1224
|
+
self.answers["answer__pti"],
|
|
1225
|
+
self.answers["answer__offset"],
|
|
1226
|
+
self.answers["answer__pd_entry"],
|
|
1227
|
+
self.answers["answer__pt_number"],
|
|
1228
|
+
self.answers["answer__pte"],
|
|
1229
|
+
self.answers["answer__is_valid"],
|
|
1230
|
+
self.answers["answer__pfn"],
|
|
1231
|
+
self.answers["answer__physical_address"],
|
|
1232
|
+
]
|
|
1233
|
+
|
|
1148
1234
|
body = ContentAST.Section()
|
|
1149
1235
|
|
|
1150
1236
|
body.add_element(
|
|
1151
1237
|
ContentAST.Paragraph([
|
|
1152
1238
|
"Given the below information please calculate the equivalent physical address of the given virtual address, filling out all steps along the way.",
|
|
1153
|
-
"This problem uses
|
|
1239
|
+
"This problem uses **two-level (hierarchical) paging**.",
|
|
1154
1240
|
"Remember, we typically have the MSB representing valid or invalid."
|
|
1155
1241
|
])
|
|
1156
1242
|
)
|
|
@@ -1245,21 +1331,27 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1245
1331
|
# Answer block
|
|
1246
1332
|
body.add_element(
|
|
1247
1333
|
ContentAST.AnswerBlock([
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1334
|
+
self.answers["answer__pdi"],
|
|
1335
|
+
self.answers["answer__pti"],
|
|
1336
|
+
self.answers["answer__offset"],
|
|
1337
|
+
self.answers["answer__pd_entry"],
|
|
1338
|
+
self.answers["answer__pt_number"],
|
|
1339
|
+
self.answers["answer__pte"],
|
|
1340
|
+
self.answers["answer__is_valid"],
|
|
1341
|
+
self.answers["answer__pfn"],
|
|
1342
|
+
self.answers["answer__physical_address"],
|
|
1257
1343
|
])
|
|
1258
1344
|
)
|
|
1259
1345
|
|
|
1346
|
+
return body, answers
|
|
1347
|
+
|
|
1348
|
+
def get_body(self, *args, **kwargs) -> ContentAST.Section:
|
|
1349
|
+
"""Build question body (backward compatible interface)."""
|
|
1350
|
+
body, _ = self._get_body(*args, **kwargs)
|
|
1260
1351
|
return body
|
|
1261
1352
|
|
|
1262
|
-
def
|
|
1353
|
+
def _get_explanation(self, *args, **kwargs):
|
|
1354
|
+
"""Build question explanation."""
|
|
1263
1355
|
explanation = ContentAST.Section()
|
|
1264
1356
|
|
|
1265
1357
|
explanation.add_element(
|
|
@@ -1278,7 +1370,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1278
1370
|
# Step 1: Extract PDI, PTI, Offset
|
|
1279
1371
|
explanation.add_element(
|
|
1280
1372
|
ContentAST.Paragraph([
|
|
1281
|
-
f"
|
|
1373
|
+
f"**Step 1: Extract components from Virtual Address**",
|
|
1282
1374
|
f"Virtual Address = PDI | PTI | Offset",
|
|
1283
1375
|
f"<tt>0b{self.virtual_address:0{self.num_bits_vpn + self.num_bits_offset}b}</tt> = "
|
|
1284
1376
|
f"<tt>0b{self.pdi:0{self.num_bits_pdi}b}</tt> | "
|
|
@@ -1290,7 +1382,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1290
1382
|
# Step 2: Look up PD Entry
|
|
1291
1383
|
explanation.add_element(
|
|
1292
1384
|
ContentAST.Paragraph([
|
|
1293
|
-
f"
|
|
1385
|
+
f"**Step 2: Look up Page Directory Entry**",
|
|
1294
1386
|
f"Using PDI = <tt>0b{self.pdi:0{self.num_bits_pdi}b}</tt>, we find PD Entry = <tt>0b{self.pd_entry:0{self.num_bits_pfn + 1}b}</tt>"
|
|
1295
1387
|
])
|
|
1296
1388
|
)
|
|
@@ -1299,8 +1391,8 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1299
1391
|
pd_valid_bit = self.pd_entry // (2 ** self.num_bits_pfn)
|
|
1300
1392
|
explanation.add_element(
|
|
1301
1393
|
ContentAST.Paragraph([
|
|
1302
|
-
f"
|
|
1303
|
-
f"The MSB (valid bit) is
|
|
1394
|
+
f"**Step 3: Check Page Directory Entry validity**",
|
|
1395
|
+
f"The MSB (valid bit) is **{pd_valid_bit}**, so this PD Entry is **{'VALID' if self.pd_valid else 'INVALID'}**."
|
|
1304
1396
|
])
|
|
1305
1397
|
)
|
|
1306
1398
|
|
|
@@ -1308,7 +1400,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1308
1400
|
explanation.add_element(
|
|
1309
1401
|
ContentAST.Paragraph([
|
|
1310
1402
|
"Since the Page Directory Entry is invalid, the translation fails here.",
|
|
1311
|
-
"We write
|
|
1403
|
+
"We write **INVALID** for all remaining fields.",
|
|
1312
1404
|
"If it were valid, we would continue with the steps below.",
|
|
1313
1405
|
"<hr>"
|
|
1314
1406
|
])
|
|
@@ -1317,7 +1409,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1317
1409
|
# Step 4: Extract PT number (if PD valid)
|
|
1318
1410
|
explanation.add_element(
|
|
1319
1411
|
ContentAST.Paragraph([
|
|
1320
|
-
f"
|
|
1412
|
+
f"**Step 4: Extract Page Table Number**",
|
|
1321
1413
|
"We remove the valid bit from the PD Entry to get the Page Table Number:"
|
|
1322
1414
|
])
|
|
1323
1415
|
)
|
|
@@ -1333,14 +1425,14 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1333
1425
|
if self.pd_valid:
|
|
1334
1426
|
explanation.add_element(
|
|
1335
1427
|
ContentAST.Paragraph([
|
|
1336
|
-
f"This tells us to use
|
|
1428
|
+
f"This tells us to use **Page Table #{self.page_table_number}**."
|
|
1337
1429
|
])
|
|
1338
1430
|
)
|
|
1339
1431
|
|
|
1340
1432
|
# Step 5: Look up PTE
|
|
1341
1433
|
explanation.add_element(
|
|
1342
1434
|
ContentAST.Paragraph([
|
|
1343
|
-
f"
|
|
1435
|
+
f"**Step 5: Look up Page Table Entry**",
|
|
1344
1436
|
f"Using PTI = <tt>0b{self.pti:0{self.num_bits_pti}b}</tt> in Page Table #{self.page_table_number}, "
|
|
1345
1437
|
f"we find PTE = <tt>0b{self.pte:0{self.num_bits_pfn + 1}b}</tt>"
|
|
1346
1438
|
])
|
|
@@ -1350,8 +1442,8 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1350
1442
|
pt_valid_bit = self.pte // (2 ** self.num_bits_pfn)
|
|
1351
1443
|
explanation.add_element(
|
|
1352
1444
|
ContentAST.Paragraph([
|
|
1353
|
-
f"
|
|
1354
|
-
f"The MSB (valid bit) is
|
|
1445
|
+
f"**Step 6: Check Page Table Entry validity**",
|
|
1446
|
+
f"The MSB (valid bit) is **{pt_valid_bit}**, so this PTE is **{'VALID' if self.pt_valid else 'INVALID'}**."
|
|
1355
1447
|
])
|
|
1356
1448
|
)
|
|
1357
1449
|
|
|
@@ -1359,7 +1451,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1359
1451
|
explanation.add_element(
|
|
1360
1452
|
ContentAST.Paragraph([
|
|
1361
1453
|
"Since the Page Table Entry is invalid, the translation fails.",
|
|
1362
|
-
"We write
|
|
1454
|
+
"We write **INVALID** for PFN and Physical Address.",
|
|
1363
1455
|
"If it were valid, we would continue with the steps below.",
|
|
1364
1456
|
"<hr>"
|
|
1365
1457
|
])
|
|
@@ -1368,7 +1460,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1368
1460
|
# Step 7: Extract PFN
|
|
1369
1461
|
explanation.add_element(
|
|
1370
1462
|
ContentAST.Paragraph([
|
|
1371
|
-
f"
|
|
1463
|
+
f"**Step 7: Extract PFN**",
|
|
1372
1464
|
"We remove the valid bit from the PTE to get the PFN:"
|
|
1373
1465
|
])
|
|
1374
1466
|
)
|
|
@@ -1384,7 +1476,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1384
1476
|
# Step 8: Construct physical address
|
|
1385
1477
|
explanation.add_element(
|
|
1386
1478
|
ContentAST.Paragraph([
|
|
1387
|
-
f"
|
|
1479
|
+
f"**Step 8: Construct Physical Address**",
|
|
1388
1480
|
"Physical Address = PFN | Offset"
|
|
1389
1481
|
])
|
|
1390
1482
|
)
|
|
@@ -1397,4 +1489,9 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1397
1489
|
)
|
|
1398
1490
|
)
|
|
1399
1491
|
|
|
1492
|
+
return explanation, []
|
|
1493
|
+
|
|
1494
|
+
def get_explanation(self, *args, **kwargs) -> ContentAST.Section:
|
|
1495
|
+
"""Build question explanation (backward compatible interface)."""
|
|
1496
|
+
explanation, _ = self._get_explanation(*args, **kwargs)
|
|
1400
1497
|
return explanation
|