QuizGenerator 0.4.4__py3-none-any.whl → 0.5.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 +949 -80
- QuizGenerator/generate.py +44 -7
- QuizGenerator/misc.py +4 -554
- QuizGenerator/mixins.py +47 -25
- QuizGenerator/premade_questions/cst334/languages.py +139 -125
- QuizGenerator/premade_questions/cst334/math_questions.py +78 -66
- QuizGenerator/premade_questions/cst334/memory_questions.py +258 -144
- QuizGenerator/premade_questions/cst334/persistence_questions.py +71 -33
- QuizGenerator/premade_questions/cst334/process.py +51 -20
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +32 -6
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +59 -34
- QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +27 -8
- QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +53 -32
- QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +228 -88
- QuizGenerator/premade_questions/cst463/models/attention.py +26 -10
- QuizGenerator/premade_questions/cst463/models/cnns.py +32 -19
- QuizGenerator/premade_questions/cst463/models/rnns.py +25 -12
- QuizGenerator/premade_questions/cst463/models/text.py +26 -11
- QuizGenerator/premade_questions/cst463/models/weight_counting.py +36 -22
- QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +89 -109
- QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +117 -51
- QuizGenerator/question.py +110 -15
- QuizGenerator/quiz.py +74 -23
- QuizGenerator/regenerate.py +98 -29
- {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.0.dist-info}/METADATA +1 -1
- {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.0.dist-info}/RECORD +29 -31
- QuizGenerator/README.md +0 -5
- QuizGenerator/logging.yaml +0 -55
- {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.0.dist-info}/WHEEL +0 -0
- {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.0.dist-info}/entry_points.txt +0 -0
- {quizgenerator-0.4.4.dist-info → quizgenerator-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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: Answer.integer("answer__num_bits_va", self.num_bits_va),
|
|
44
|
-
self.Target.OFFSET_BITS: Answer.integer("answer__num_bits_offset", self.num_bits_offset),
|
|
45
|
-
self.Target.VPN_BITS: Answer.integer("answer__num_bits_vpn", self.num_bits_vpn)
|
|
43
|
+
self.Target.VA_BITS: Answer.integer("answer__num_bits_va", self.num_bits_va, unit="bits"),
|
|
44
|
+
self.Target.OFFSET_BITS: Answer.integer("answer__num_bits_offset", self.num_bits_offset, unit="bits"),
|
|
45
|
+
self.Target.VPN_BITS: Answer.integer("answer__num_bits_vpn", 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
|
|
|
@@ -245,14 +259,21 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
245
259
|
self.hit_rate = 100 * number_of_hits / (self.num_requests)
|
|
246
260
|
self.answers.update(
|
|
247
261
|
{
|
|
248
|
-
"answer__hit_rate": Answer.auto_float(
|
|
262
|
+
"answer__hit_rate": Answer.auto_float(
|
|
263
|
+
"answer__hit_rate", self.hit_rate,
|
|
264
|
+
label=f"Hit rate, excluding non-capacity misses. Round to {Answer.DEFAULT_ROUNDING_DIGITS} decimal digits if appropriate.",
|
|
265
|
+
unit="%"
|
|
266
|
+
)
|
|
249
267
|
}
|
|
250
268
|
)
|
|
251
269
|
|
|
252
270
|
# Return whether this workload is interesting
|
|
253
271
|
return self.is_interesting()
|
|
254
272
|
|
|
255
|
-
def
|
|
273
|
+
def _get_body(self, **kwargs):
|
|
274
|
+
"""Build question body and collect answers."""
|
|
275
|
+
answers = []
|
|
276
|
+
|
|
256
277
|
# Create table data for cache simulation
|
|
257
278
|
table_rows = []
|
|
258
279
|
for request_number in sorted(self.request_results.keys()):
|
|
@@ -264,47 +285,55 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
264
285
|
"Cache State": f"answer__cache_state-{request_number}" # Answer key
|
|
265
286
|
}
|
|
266
287
|
)
|
|
267
|
-
|
|
288
|
+
# Collect answers for this request
|
|
289
|
+
answers.append(self.answers[f"answer__hit-{request_number}"])
|
|
290
|
+
answers.append(self.answers[f"answer__evicted-{request_number}"])
|
|
291
|
+
answers.append(self.answers[f"answer__cache_state-{request_number}"])
|
|
292
|
+
|
|
268
293
|
# Create table using mixin - automatically handles answer conversion
|
|
269
294
|
cache_table = self.create_answer_table(
|
|
270
295
|
headers=["Page Requested", "Hit/Miss", "Evicted", "Cache State"],
|
|
271
296
|
data_rows=table_rows,
|
|
272
297
|
answer_columns=["Hit/Miss", "Evicted", "Cache State"]
|
|
273
298
|
)
|
|
274
|
-
|
|
299
|
+
|
|
300
|
+
# Collect hit rate answer
|
|
301
|
+
hit_rate_answer = self.answers["answer__hit_rate"]
|
|
302
|
+
answers.append(hit_rate_answer)
|
|
303
|
+
|
|
275
304
|
# 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
|
-
|
|
305
|
+
hit_rate_block = ContentAST.AnswerBlock(hit_rate_answer)
|
|
306
|
+
|
|
284
307
|
# Use mixin to create complete body
|
|
285
308
|
intro_text = (
|
|
286
|
-
f"Assume we are using a
|
|
309
|
+
f"Assume we are using a **{self.cache_policy}** caching policy and a cache size of **{self.cache_size}**. "
|
|
287
310
|
"Given the below series of requests please fill in the table. "
|
|
288
311
|
"For the hit/miss column, please write either \"hit\" or \"miss\". "
|
|
289
312
|
"For the eviction column, please write either the number of the evicted page or simply a dash (e.g. \"-\")."
|
|
290
313
|
)
|
|
291
|
-
|
|
314
|
+
|
|
292
315
|
instructions = ContentAST.OnlyHtml([
|
|
293
316
|
"For the cache state, please enter the cache contents in the order suggested in class, "
|
|
294
317
|
"which means separated by commas with spaces (e.g. \"1, 2, 3\") "
|
|
295
318
|
"and with the left-most being the next to be evicted. "
|
|
296
319
|
"In the case where there is a tie, order by increasing number."
|
|
297
320
|
])
|
|
298
|
-
|
|
321
|
+
|
|
299
322
|
body = self.create_fill_in_table_body(intro_text, instructions, cache_table)
|
|
300
323
|
body.add_element(hit_rate_block)
|
|
324
|
+
return body, answers
|
|
325
|
+
|
|
326
|
+
def get_body(self, **kwargs) -> ContentAST.Section:
|
|
327
|
+
"""Build question body (backward compatible interface)."""
|
|
328
|
+
body, _ = self._get_body(**kwargs)
|
|
301
329
|
return body
|
|
302
|
-
|
|
303
|
-
def
|
|
330
|
+
|
|
331
|
+
def _get_explanation(self, **kwargs):
|
|
332
|
+
"""Build question explanation."""
|
|
304
333
|
explanation = ContentAST.Section()
|
|
305
|
-
|
|
334
|
+
|
|
306
335
|
explanation.add_element(ContentAST.Paragraph(["The full caching table can be seen below."]))
|
|
307
|
-
|
|
336
|
+
|
|
308
337
|
explanation.add_element(
|
|
309
338
|
ContentAST.Table(
|
|
310
339
|
headers=["Page", "Hit/Miss", "Evicted", "Cache State"],
|
|
@@ -319,7 +348,7 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
319
348
|
]
|
|
320
349
|
)
|
|
321
350
|
)
|
|
322
|
-
|
|
351
|
+
|
|
323
352
|
explanation.add_element(
|
|
324
353
|
ContentAST.Paragraph(
|
|
325
354
|
[
|
|
@@ -330,7 +359,12 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
330
359
|
]
|
|
331
360
|
)
|
|
332
361
|
)
|
|
333
|
-
|
|
362
|
+
|
|
363
|
+
return explanation, []
|
|
364
|
+
|
|
365
|
+
def get_explanation(self, **kwargs) -> ContentAST.Section:
|
|
366
|
+
"""Build question explanation (backward compatible interface)."""
|
|
367
|
+
explanation, _ = self._get_explanation(**kwargs)
|
|
334
368
|
return explanation
|
|
335
369
|
|
|
336
370
|
def is_interesting(self) -> bool:
|
|
@@ -373,22 +407,25 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
373
407
|
else:
|
|
374
408
|
self.answers["answer"] = Answer.string("answer__physical_address", "INVALID")
|
|
375
409
|
|
|
376
|
-
def
|
|
410
|
+
def _get_body(self):
|
|
411
|
+
"""Build question body and collect answers."""
|
|
412
|
+
answers = [self.answers["answer"]]
|
|
413
|
+
|
|
377
414
|
# Use mixin to create parameter table with answer
|
|
378
415
|
parameter_info = {
|
|
379
416
|
"Base": f"0x{self.base:X}",
|
|
380
417
|
"Bounds": f"0x{self.bounds:X}",
|
|
381
418
|
"Virtual Address": f"0x{self.virtual_address:X}"
|
|
382
419
|
}
|
|
383
|
-
|
|
420
|
+
|
|
384
421
|
table = self.create_parameter_answer_table(
|
|
385
422
|
parameter_info=parameter_info,
|
|
386
423
|
answer_label="Physical Address",
|
|
387
424
|
answer_key="answer",
|
|
388
425
|
transpose=True
|
|
389
426
|
)
|
|
390
|
-
|
|
391
|
-
|
|
427
|
+
|
|
428
|
+
body = self.create_parameter_calculation_body(
|
|
392
429
|
intro_text=(
|
|
393
430
|
"Given the information in the below table, "
|
|
394
431
|
"please calcuate the physical address associated with the given virtual address. "
|
|
@@ -396,10 +433,17 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
396
433
|
),
|
|
397
434
|
parameter_table=table
|
|
398
435
|
)
|
|
399
|
-
|
|
400
|
-
|
|
436
|
+
return body, answers
|
|
437
|
+
|
|
438
|
+
def get_body(self) -> ContentAST.Section:
|
|
439
|
+
"""Build question body (backward compatible interface)."""
|
|
440
|
+
body, _ = self._get_body()
|
|
441
|
+
return body
|
|
442
|
+
|
|
443
|
+
def _get_explanation(self):
|
|
444
|
+
"""Build question explanation."""
|
|
401
445
|
explanation = ContentAST.Section()
|
|
402
|
-
|
|
446
|
+
|
|
403
447
|
explanation.add_element(
|
|
404
448
|
ContentAST.Paragraph(
|
|
405
449
|
[
|
|
@@ -410,7 +454,7 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
410
454
|
]
|
|
411
455
|
)
|
|
412
456
|
)
|
|
413
|
-
|
|
457
|
+
|
|
414
458
|
explanation.add_element(
|
|
415
459
|
ContentAST.Paragraph(
|
|
416
460
|
[
|
|
@@ -419,7 +463,7 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
419
463
|
]
|
|
420
464
|
)
|
|
421
465
|
)
|
|
422
|
-
|
|
466
|
+
|
|
423
467
|
if self.virtual_address < self.bounds:
|
|
424
468
|
explanation.add_element(
|
|
425
469
|
ContentAST.Paragraph(
|
|
@@ -442,7 +486,12 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
442
486
|
]
|
|
443
487
|
)
|
|
444
488
|
)
|
|
445
|
-
|
|
489
|
+
|
|
490
|
+
return explanation, []
|
|
491
|
+
|
|
492
|
+
def get_explanation(self) -> ContentAST.Section:
|
|
493
|
+
"""Build question explanation (backward compatible interface)."""
|
|
494
|
+
explanation, _ = self._get_explanation()
|
|
446
495
|
return explanation
|
|
447
496
|
|
|
448
497
|
|
|
@@ -540,19 +589,28 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
540
589
|
self.answers["answer__physical_address"] = Answer.binary_hex(
|
|
541
590
|
"answer__physical_address",
|
|
542
591
|
self.physical_address,
|
|
543
|
-
length=self.physical_bits
|
|
592
|
+
length=self.physical_bits,
|
|
593
|
+
label="Physical Address"
|
|
544
594
|
)
|
|
545
595
|
else:
|
|
546
596
|
self.answers["answer__physical_address"] = Answer.string(
|
|
547
597
|
"answer__physical_address",
|
|
548
|
-
"INVALID"
|
|
598
|
+
"INVALID",
|
|
599
|
+
label="Physical Address"
|
|
549
600
|
)
|
|
550
|
-
|
|
551
|
-
self.answers["answer__segment"] = Answer.string("answer__segment", self.segment
|
|
601
|
+
|
|
602
|
+
self.answers["answer__segment"] = Answer.string("answer__segment", self.segment,
|
|
603
|
+
label="Segment name")
|
|
552
604
|
|
|
553
|
-
def
|
|
605
|
+
def _get_body(self):
|
|
606
|
+
"""Build question body and collect answers."""
|
|
607
|
+
answers = [
|
|
608
|
+
self.answers["answer__segment"],
|
|
609
|
+
self.answers["answer__physical_address"]
|
|
610
|
+
]
|
|
611
|
+
|
|
554
612
|
body = ContentAST.Section()
|
|
555
|
-
|
|
613
|
+
|
|
556
614
|
body.add_element(
|
|
557
615
|
ContentAST.Paragraph(
|
|
558
616
|
[
|
|
@@ -565,39 +623,36 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
565
623
|
]
|
|
566
624
|
)
|
|
567
625
|
)
|
|
568
|
-
|
|
626
|
+
|
|
569
627
|
# Create segment table using mixin
|
|
570
628
|
segment_rows = [
|
|
571
629
|
{"": "code", "base": f"0b{self.base['code']:0{self.physical_bits}b}", "bounds": f"0b{self.bounds['code']:0b}"},
|
|
572
630
|
{"": "heap", "base": f"0b{self.base['heap']:0{self.physical_bits}b}", "bounds": f"0b{self.bounds['heap']:0b}"},
|
|
573
631
|
{"": "stack", "base": f"0b{self.base['stack']:0{self.physical_bits}b}", "bounds": f"0b{self.bounds['stack']:0b}"}
|
|
574
632
|
]
|
|
575
|
-
|
|
633
|
+
|
|
576
634
|
segment_table = self.create_answer_table(
|
|
577
635
|
headers=["", "base", "bounds"],
|
|
578
636
|
data_rows=segment_rows,
|
|
579
637
|
answer_columns=[] # No answer columns in this table
|
|
580
638
|
)
|
|
581
|
-
|
|
639
|
+
|
|
582
640
|
body.add_element(segment_table)
|
|
583
|
-
|
|
641
|
+
|
|
584
642
|
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
|
-
)
|
|
643
|
+
ContentAST.AnswerBlock([
|
|
644
|
+
self.answers["answer__segment"],
|
|
645
|
+
self.answers["answer__physical_address"]
|
|
646
|
+
])
|
|
597
647
|
)
|
|
648
|
+
return body, answers
|
|
649
|
+
|
|
650
|
+
def get_body(self) -> ContentAST.Section:
|
|
651
|
+
"""Build question body (backward compatible interface)."""
|
|
652
|
+
body, _ = self._get_body()
|
|
598
653
|
return body
|
|
599
|
-
|
|
600
|
-
def
|
|
654
|
+
|
|
655
|
+
def _get_explanation(self):
|
|
601
656
|
explanation = ContentAST.Section()
|
|
602
657
|
|
|
603
658
|
explanation.add_element(
|
|
@@ -691,7 +746,12 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
691
746
|
f" 0b{self.physical_address:0{self.physical_bits}b}\n"
|
|
692
747
|
)
|
|
693
748
|
)
|
|
694
|
-
|
|
749
|
+
|
|
750
|
+
return explanation, []
|
|
751
|
+
|
|
752
|
+
def get_explanation(self) -> ContentAST.Section:
|
|
753
|
+
"""Build question explanation (backward compatible interface)."""
|
|
754
|
+
explanation, _ = self._get_explanation()
|
|
695
755
|
return explanation
|
|
696
756
|
|
|
697
757
|
|
|
@@ -765,34 +825,45 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
765
825
|
|
|
766
826
|
self.answers.update(
|
|
767
827
|
{
|
|
768
|
-
"answer__vpn": Answer.binary_hex("answer__vpn", self.vpn, length=self.num_bits_vpn),
|
|
769
|
-
"answer__offset": Answer.binary_hex("answer__offset", self.offset, length=self.num_bits_offset),
|
|
770
|
-
"answer__pte": Answer.binary_hex("answer__pte", self.pte, length=(self.num_bits_pfn + 1)),
|
|
828
|
+
"answer__vpn": Answer.binary_hex("answer__vpn", self.vpn, length=self.num_bits_vpn, label="VPN"),
|
|
829
|
+
"answer__offset": Answer.binary_hex("answer__offset", self.offset, length=self.num_bits_offset, label="Offset"),
|
|
830
|
+
"answer__pte": Answer.binary_hex("answer__pte", self.pte, length=(self.num_bits_pfn + 1), label="PTE"),
|
|
771
831
|
}
|
|
772
832
|
)
|
|
773
833
|
|
|
774
834
|
if self.is_valid:
|
|
775
835
|
self.answers.update(
|
|
776
836
|
{
|
|
777
|
-
"answer__is_valid": Answer.string("answer__is_valid", "VALID"),
|
|
778
|
-
"answer__pfn": Answer.binary_hex("answer__pfn", self.pfn, length=self.num_bits_pfn),
|
|
837
|
+
"answer__is_valid": Answer.string("answer__is_valid", "VALID", label="VALID or INVALID?"),
|
|
838
|
+
"answer__pfn": Answer.binary_hex("answer__pfn", self.pfn, length=self.num_bits_pfn, label="PFN"),
|
|
779
839
|
"answer__physical_address": Answer.binary_hex(
|
|
780
|
-
"answer__physical_address", self.physical_address,
|
|
840
|
+
"answer__physical_address", self.physical_address,
|
|
841
|
+
length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
781
842
|
),
|
|
782
843
|
}
|
|
783
844
|
)
|
|
784
845
|
else:
|
|
785
846
|
self.answers.update(
|
|
786
847
|
{
|
|
787
|
-
"answer__is_valid": Answer.string("answer__is_valid", "INVALID"),
|
|
788
|
-
"answer__pfn": Answer.string("answer__pfn", "INVALID"),
|
|
789
|
-
"answer__physical_address": Answer.string("answer__physical_address", "INVALID"),
|
|
848
|
+
"answer__is_valid": Answer.string("answer__is_valid", "INVALID", label="VALID or INVALID?"),
|
|
849
|
+
"answer__pfn": Answer.string("answer__pfn", "INVALID", label="PFN"),
|
|
850
|
+
"answer__physical_address": Answer.string("answer__physical_address", "INVALID", label="Physical Address"),
|
|
790
851
|
}
|
|
791
852
|
)
|
|
792
853
|
|
|
793
|
-
def
|
|
854
|
+
def _get_body(self, *args, **kwargs):
|
|
855
|
+
"""Build question body and collect answers."""
|
|
856
|
+
answers = [
|
|
857
|
+
self.answers["answer__vpn"],
|
|
858
|
+
self.answers["answer__offset"],
|
|
859
|
+
self.answers["answer__pte"],
|
|
860
|
+
self.answers["answer__is_valid"],
|
|
861
|
+
self.answers["answer__pfn"],
|
|
862
|
+
self.answers["answer__physical_address"],
|
|
863
|
+
]
|
|
864
|
+
|
|
794
865
|
body = ContentAST.Section()
|
|
795
|
-
|
|
866
|
+
|
|
796
867
|
body.add_element(
|
|
797
868
|
ContentAST.Paragraph(
|
|
798
869
|
[
|
|
@@ -801,14 +872,14 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
801
872
|
]
|
|
802
873
|
)
|
|
803
874
|
)
|
|
804
|
-
|
|
875
|
+
|
|
805
876
|
# Create parameter info table using mixin
|
|
806
877
|
parameter_info = {
|
|
807
878
|
"Virtual Address": f"0b{self.virtual_address:0{self.num_bits_vpn + self.num_bits_offset}b}",
|
|
808
879
|
"# VPN bits": f"{self.num_bits_vpn}",
|
|
809
880
|
"# PFN bits": f"{self.num_bits_pfn}"
|
|
810
881
|
}
|
|
811
|
-
|
|
882
|
+
|
|
812
883
|
body.add_element(self.create_info_table(parameter_info))
|
|
813
884
|
|
|
814
885
|
# Use the page table generated in refresh() for deterministic output
|
|
@@ -827,33 +898,36 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
827
898
|
|
|
828
899
|
if (max(self.page_table.keys()) + 1) != 2 ** self.num_bits_vpn:
|
|
829
900
|
value_matrix.append(["...", "..."])
|
|
830
|
-
|
|
901
|
+
|
|
831
902
|
body.add_element(
|
|
832
903
|
ContentAST.Table(
|
|
833
904
|
headers=["VPN", "PTE"],
|
|
834
905
|
data=value_matrix
|
|
835
906
|
)
|
|
836
907
|
)
|
|
837
|
-
|
|
908
|
+
|
|
838
909
|
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
|
-
)
|
|
910
|
+
ContentAST.AnswerBlock([
|
|
911
|
+
self.answers["answer__vpn"],
|
|
912
|
+
self.answers["answer__offset"],
|
|
913
|
+
self.answers["answer__pte"],
|
|
914
|
+
self.answers["answer__is_valid"],
|
|
915
|
+
self.answers["answer__pfn"],
|
|
916
|
+
self.answers["answer__physical_address"],
|
|
917
|
+
])
|
|
850
918
|
)
|
|
851
|
-
|
|
919
|
+
|
|
920
|
+
return body, answers
|
|
921
|
+
|
|
922
|
+
def get_body(self, *args, **kwargs) -> ContentAST.Section:
|
|
923
|
+
"""Build question body (backward compatible interface)."""
|
|
924
|
+
body, _ = self._get_body(*args, **kwargs)
|
|
852
925
|
return body
|
|
853
926
|
|
|
854
|
-
def
|
|
927
|
+
def _get_explanation(self, *args, **kwargs):
|
|
928
|
+
"""Build question explanation."""
|
|
855
929
|
explanation = ContentAST.Section()
|
|
856
|
-
|
|
930
|
+
|
|
857
931
|
explanation.add_element(
|
|
858
932
|
ContentAST.Paragraph(
|
|
859
933
|
[
|
|
@@ -863,7 +937,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
863
937
|
]
|
|
864
938
|
)
|
|
865
939
|
)
|
|
866
|
-
|
|
940
|
+
|
|
867
941
|
explanation.add_element(
|
|
868
942
|
ContentAST.Paragraph(
|
|
869
943
|
[
|
|
@@ -871,7 +945,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
871
945
|
]
|
|
872
946
|
)
|
|
873
947
|
)
|
|
874
|
-
|
|
948
|
+
|
|
875
949
|
explanation.add_element(
|
|
876
950
|
ContentAST.Paragraph(
|
|
877
951
|
[
|
|
@@ -881,7 +955,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
881
955
|
]
|
|
882
956
|
)
|
|
883
957
|
)
|
|
884
|
-
|
|
958
|
+
|
|
885
959
|
explanation.add_element(
|
|
886
960
|
ContentAST.Paragraph(
|
|
887
961
|
[
|
|
@@ -892,12 +966,12 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
892
966
|
]
|
|
893
967
|
)
|
|
894
968
|
)
|
|
895
|
-
|
|
969
|
+
|
|
896
970
|
if self.is_valid:
|
|
897
971
|
explanation.add_element(
|
|
898
972
|
ContentAST.Paragraph(
|
|
899
973
|
[
|
|
900
|
-
f"In our PTE we see that the first bit is
|
|
974
|
+
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
975
|
]
|
|
902
976
|
)
|
|
903
977
|
)
|
|
@@ -905,14 +979,14 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
905
979
|
explanation.add_element(
|
|
906
980
|
ContentAST.Paragraph(
|
|
907
981
|
[
|
|
908
|
-
f"In our PTE we see that the first bit is
|
|
982
|
+
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
983
|
"Therefore, we just write \"INVALID\" as our answer.",
|
|
910
984
|
"If it were valid we would complete the below steps.",
|
|
911
985
|
"<hr>"
|
|
912
986
|
]
|
|
913
987
|
)
|
|
914
988
|
)
|
|
915
|
-
|
|
989
|
+
|
|
916
990
|
explanation.add_element(
|
|
917
991
|
ContentAST.Paragraph(
|
|
918
992
|
[
|
|
@@ -930,7 +1004,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
930
1004
|
f"\\& \\texttt{{0b{(2 ** self.num_bits_pfn) - 1:0{self.num_bits_pfn + 1}b}}}"
|
|
931
1005
|
)
|
|
932
1006
|
)
|
|
933
|
-
|
|
1007
|
+
|
|
934
1008
|
explanation.add_elements(
|
|
935
1009
|
[
|
|
936
1010
|
ContentAST.Paragraph(
|
|
@@ -944,7 +1018,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
944
1018
|
)
|
|
945
1019
|
]
|
|
946
1020
|
)
|
|
947
|
-
|
|
1021
|
+
|
|
948
1022
|
explanation.add_elements(
|
|
949
1023
|
[
|
|
950
1024
|
ContentAST.Paragraph(["Note: Strictly speaking, this calculation is:", ]),
|
|
@@ -955,6 +1029,11 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
955
1029
|
]
|
|
956
1030
|
)
|
|
957
1031
|
|
|
1032
|
+
return explanation, []
|
|
1033
|
+
|
|
1034
|
+
def get_explanation(self, *args, **kwargs) -> ContentAST.Section:
|
|
1035
|
+
"""Build question explanation (backward compatible interface)."""
|
|
1036
|
+
explanation, _ = self._get_explanation(*args, **kwargs)
|
|
958
1037
|
return explanation
|
|
959
1038
|
|
|
960
1039
|
|
|
@@ -1108,49 +1187,73 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1108
1187
|
|
|
1109
1188
|
# Set up answers
|
|
1110
1189
|
self.answers.update({
|
|
1111
|
-
"answer__pdi": Answer.binary_hex("answer__pdi", self.pdi, length=self.num_bits_pdi
|
|
1112
|
-
|
|
1113
|
-
"
|
|
1114
|
-
|
|
1115
|
-
"
|
|
1190
|
+
"answer__pdi": Answer.binary_hex("answer__pdi", self.pdi, length=self.num_bits_pdi,
|
|
1191
|
+
label="PDI (Page Directory Index)"),
|
|
1192
|
+
"answer__pti": Answer.binary_hex("answer__pti", self.pti, length=self.num_bits_pti,
|
|
1193
|
+
label="PTI (Page Table Index)"),
|
|
1194
|
+
"answer__offset": Answer.binary_hex("answer__offset", self.offset, length=self.num_bits_offset,
|
|
1195
|
+
label="Offset"),
|
|
1196
|
+
"answer__pd_entry": Answer.binary_hex("answer__pd_entry", self.pd_entry, length=(self.num_bits_pfn + 1),
|
|
1197
|
+
label="PD Entry (from Page Directory)"),
|
|
1198
|
+
"answer__pt_number": (
|
|
1199
|
+
Answer.binary_hex("answer__pt_number", self.page_table_number, length=self.num_bits_pfn,
|
|
1200
|
+
label="Page Table Number")
|
|
1201
|
+
if self.pd_valid
|
|
1202
|
+
else Answer.string("answer__pt_number", "INVALID", label="Page Table Number")
|
|
1203
|
+
),
|
|
1116
1204
|
})
|
|
1117
1205
|
|
|
1118
1206
|
# PTE answer: if PD is valid, accept the actual PTE value from the table
|
|
1119
1207
|
# (regardless of whether that PTE is valid or invalid)
|
|
1120
1208
|
if self.pd_valid:
|
|
1121
1209
|
self.answers.update({
|
|
1122
|
-
"answer__pte": Answer.binary_hex("answer__pte", self.pte, length=(self.num_bits_pfn + 1)
|
|
1210
|
+
"answer__pte": Answer.binary_hex("answer__pte", self.pte, length=(self.num_bits_pfn + 1),
|
|
1211
|
+
label="PTE (from Page Table)"),
|
|
1123
1212
|
})
|
|
1124
1213
|
else:
|
|
1125
1214
|
# If PD is invalid, student can't look up the page table
|
|
1126
1215
|
# Accept both "INVALID" (for consistency) and "N/A" (for accuracy)
|
|
1127
1216
|
self.answers.update({
|
|
1128
|
-
"answer__pte": Answer.string("answer__pte", ["INVALID", "N/A"]),
|
|
1217
|
+
"answer__pte": Answer.string("answer__pte", ["INVALID", "N/A"], label="PTE (from Page Table)"),
|
|
1129
1218
|
})
|
|
1130
1219
|
|
|
1131
1220
|
# Validity, PFN, and Physical Address depend on BOTH levels being valid
|
|
1132
1221
|
if self.pd_valid and self.pt_valid:
|
|
1133
1222
|
self.answers.update({
|
|
1134
|
-
"answer__is_valid": Answer.string("answer__is_valid", "VALID"),
|
|
1135
|
-
"answer__pfn": Answer.binary_hex("answer__pfn", self.pfn, length=self.num_bits_pfn),
|
|
1223
|
+
"answer__is_valid": Answer.string("answer__is_valid", "VALID", label="VALID or INVALID?"),
|
|
1224
|
+
"answer__pfn": Answer.binary_hex("answer__pfn", self.pfn, length=self.num_bits_pfn, label="PFN"),
|
|
1136
1225
|
"answer__physical_address": Answer.binary_hex(
|
|
1137
|
-
"answer__physical_address", self.physical_address,
|
|
1226
|
+
"answer__physical_address", self.physical_address,
|
|
1227
|
+
length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
1138
1228
|
),
|
|
1139
1229
|
})
|
|
1140
1230
|
else:
|
|
1141
1231
|
self.answers.update({
|
|
1142
|
-
"answer__is_valid": Answer.string("answer__is_valid", "INVALID"),
|
|
1143
|
-
"answer__pfn": Answer.string("answer__pfn", "INVALID"),
|
|
1144
|
-
"answer__physical_address": Answer.string("answer__physical_address", "INVALID"),
|
|
1232
|
+
"answer__is_valid": Answer.string("answer__is_valid", "INVALID", label="VALID or INVALID?"),
|
|
1233
|
+
"answer__pfn": Answer.string("answer__pfn", "INVALID", label="PFN"),
|
|
1234
|
+
"answer__physical_address": Answer.string("answer__physical_address", "INVALID", label="Physical Address"),
|
|
1145
1235
|
})
|
|
1146
1236
|
|
|
1147
|
-
def
|
|
1237
|
+
def _get_body(self, *args, **kwargs):
|
|
1238
|
+
"""Build question body and collect answers."""
|
|
1239
|
+
answers = [
|
|
1240
|
+
self.answers["answer__pdi"],
|
|
1241
|
+
self.answers["answer__pti"],
|
|
1242
|
+
self.answers["answer__offset"],
|
|
1243
|
+
self.answers["answer__pd_entry"],
|
|
1244
|
+
self.answers["answer__pt_number"],
|
|
1245
|
+
self.answers["answer__pte"],
|
|
1246
|
+
self.answers["answer__is_valid"],
|
|
1247
|
+
self.answers["answer__pfn"],
|
|
1248
|
+
self.answers["answer__physical_address"],
|
|
1249
|
+
]
|
|
1250
|
+
|
|
1148
1251
|
body = ContentAST.Section()
|
|
1149
1252
|
|
|
1150
1253
|
body.add_element(
|
|
1151
1254
|
ContentAST.Paragraph([
|
|
1152
1255
|
"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
|
|
1256
|
+
"This problem uses **two-level (hierarchical) paging**.",
|
|
1154
1257
|
"Remember, we typically have the MSB representing valid or invalid."
|
|
1155
1258
|
])
|
|
1156
1259
|
)
|
|
@@ -1245,21 +1348,27 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1245
1348
|
# Answer block
|
|
1246
1349
|
body.add_element(
|
|
1247
1350
|
ContentAST.AnswerBlock([
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1351
|
+
self.answers["answer__pdi"],
|
|
1352
|
+
self.answers["answer__pti"],
|
|
1353
|
+
self.answers["answer__offset"],
|
|
1354
|
+
self.answers["answer__pd_entry"],
|
|
1355
|
+
self.answers["answer__pt_number"],
|
|
1356
|
+
self.answers["answer__pte"],
|
|
1357
|
+
self.answers["answer__is_valid"],
|
|
1358
|
+
self.answers["answer__pfn"],
|
|
1359
|
+
self.answers["answer__physical_address"],
|
|
1257
1360
|
])
|
|
1258
1361
|
)
|
|
1259
1362
|
|
|
1363
|
+
return body, answers
|
|
1364
|
+
|
|
1365
|
+
def get_body(self, *args, **kwargs) -> ContentAST.Section:
|
|
1366
|
+
"""Build question body (backward compatible interface)."""
|
|
1367
|
+
body, _ = self._get_body(*args, **kwargs)
|
|
1260
1368
|
return body
|
|
1261
1369
|
|
|
1262
|
-
def
|
|
1370
|
+
def _get_explanation(self, *args, **kwargs):
|
|
1371
|
+
"""Build question explanation."""
|
|
1263
1372
|
explanation = ContentAST.Section()
|
|
1264
1373
|
|
|
1265
1374
|
explanation.add_element(
|
|
@@ -1278,7 +1387,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1278
1387
|
# Step 1: Extract PDI, PTI, Offset
|
|
1279
1388
|
explanation.add_element(
|
|
1280
1389
|
ContentAST.Paragraph([
|
|
1281
|
-
f"
|
|
1390
|
+
f"**Step 1: Extract components from Virtual Address**",
|
|
1282
1391
|
f"Virtual Address = PDI | PTI | Offset",
|
|
1283
1392
|
f"<tt>0b{self.virtual_address:0{self.num_bits_vpn + self.num_bits_offset}b}</tt> = "
|
|
1284
1393
|
f"<tt>0b{self.pdi:0{self.num_bits_pdi}b}</tt> | "
|
|
@@ -1290,7 +1399,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1290
1399
|
# Step 2: Look up PD Entry
|
|
1291
1400
|
explanation.add_element(
|
|
1292
1401
|
ContentAST.Paragraph([
|
|
1293
|
-
f"
|
|
1402
|
+
f"**Step 2: Look up Page Directory Entry**",
|
|
1294
1403
|
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
1404
|
])
|
|
1296
1405
|
)
|
|
@@ -1299,8 +1408,8 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1299
1408
|
pd_valid_bit = self.pd_entry // (2 ** self.num_bits_pfn)
|
|
1300
1409
|
explanation.add_element(
|
|
1301
1410
|
ContentAST.Paragraph([
|
|
1302
|
-
f"
|
|
1303
|
-
f"The MSB (valid bit) is
|
|
1411
|
+
f"**Step 3: Check Page Directory Entry validity**",
|
|
1412
|
+
f"The MSB (valid bit) is **{pd_valid_bit}**, so this PD Entry is **{'VALID' if self.pd_valid else 'INVALID'}**."
|
|
1304
1413
|
])
|
|
1305
1414
|
)
|
|
1306
1415
|
|
|
@@ -1308,7 +1417,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1308
1417
|
explanation.add_element(
|
|
1309
1418
|
ContentAST.Paragraph([
|
|
1310
1419
|
"Since the Page Directory Entry is invalid, the translation fails here.",
|
|
1311
|
-
"We write
|
|
1420
|
+
"We write **INVALID** for all remaining fields.",
|
|
1312
1421
|
"If it were valid, we would continue with the steps below.",
|
|
1313
1422
|
"<hr>"
|
|
1314
1423
|
])
|
|
@@ -1317,7 +1426,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1317
1426
|
# Step 4: Extract PT number (if PD valid)
|
|
1318
1427
|
explanation.add_element(
|
|
1319
1428
|
ContentAST.Paragraph([
|
|
1320
|
-
f"
|
|
1429
|
+
f"**Step 4: Extract Page Table Number**",
|
|
1321
1430
|
"We remove the valid bit from the PD Entry to get the Page Table Number:"
|
|
1322
1431
|
])
|
|
1323
1432
|
)
|
|
@@ -1333,14 +1442,14 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1333
1442
|
if self.pd_valid:
|
|
1334
1443
|
explanation.add_element(
|
|
1335
1444
|
ContentAST.Paragraph([
|
|
1336
|
-
f"This tells us to use
|
|
1445
|
+
f"This tells us to use **Page Table #{self.page_table_number}**."
|
|
1337
1446
|
])
|
|
1338
1447
|
)
|
|
1339
1448
|
|
|
1340
1449
|
# Step 5: Look up PTE
|
|
1341
1450
|
explanation.add_element(
|
|
1342
1451
|
ContentAST.Paragraph([
|
|
1343
|
-
f"
|
|
1452
|
+
f"**Step 5: Look up Page Table Entry**",
|
|
1344
1453
|
f"Using PTI = <tt>0b{self.pti:0{self.num_bits_pti}b}</tt> in Page Table #{self.page_table_number}, "
|
|
1345
1454
|
f"we find PTE = <tt>0b{self.pte:0{self.num_bits_pfn + 1}b}</tt>"
|
|
1346
1455
|
])
|
|
@@ -1350,8 +1459,8 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1350
1459
|
pt_valid_bit = self.pte // (2 ** self.num_bits_pfn)
|
|
1351
1460
|
explanation.add_element(
|
|
1352
1461
|
ContentAST.Paragraph([
|
|
1353
|
-
f"
|
|
1354
|
-
f"The MSB (valid bit) is
|
|
1462
|
+
f"**Step 6: Check Page Table Entry validity**",
|
|
1463
|
+
f"The MSB (valid bit) is **{pt_valid_bit}**, so this PTE is **{'VALID' if self.pt_valid else 'INVALID'}**."
|
|
1355
1464
|
])
|
|
1356
1465
|
)
|
|
1357
1466
|
|
|
@@ -1359,7 +1468,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1359
1468
|
explanation.add_element(
|
|
1360
1469
|
ContentAST.Paragraph([
|
|
1361
1470
|
"Since the Page Table Entry is invalid, the translation fails.",
|
|
1362
|
-
"We write
|
|
1471
|
+
"We write **INVALID** for PFN and Physical Address.",
|
|
1363
1472
|
"If it were valid, we would continue with the steps below.",
|
|
1364
1473
|
"<hr>"
|
|
1365
1474
|
])
|
|
@@ -1368,7 +1477,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1368
1477
|
# Step 7: Extract PFN
|
|
1369
1478
|
explanation.add_element(
|
|
1370
1479
|
ContentAST.Paragraph([
|
|
1371
|
-
f"
|
|
1480
|
+
f"**Step 7: Extract PFN**",
|
|
1372
1481
|
"We remove the valid bit from the PTE to get the PFN:"
|
|
1373
1482
|
])
|
|
1374
1483
|
)
|
|
@@ -1384,7 +1493,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1384
1493
|
# Step 8: Construct physical address
|
|
1385
1494
|
explanation.add_element(
|
|
1386
1495
|
ContentAST.Paragraph([
|
|
1387
|
-
f"
|
|
1496
|
+
f"**Step 8: Construct Physical Address**",
|
|
1388
1497
|
"Physical Address = PFN | Offset"
|
|
1389
1498
|
])
|
|
1390
1499
|
)
|
|
@@ -1397,4 +1506,9 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1397
1506
|
)
|
|
1398
1507
|
)
|
|
1399
1508
|
|
|
1509
|
+
return explanation, []
|
|
1510
|
+
|
|
1511
|
+
def get_explanation(self, *args, **kwargs) -> ContentAST.Section:
|
|
1512
|
+
"""Build question explanation (backward compatible interface)."""
|
|
1513
|
+
explanation, _ = self._get_explanation(*args, **kwargs)
|
|
1400
1514
|
return explanation
|