QuizGenerator 0.6.2__py3-none-any.whl → 0.7.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 +2198 -2213
- QuizGenerator/misc.py +1 -1
- QuizGenerator/mixins.py +64 -64
- QuizGenerator/premade_questions/basic.py +16 -16
- QuizGenerator/premade_questions/cst334/languages.py +26 -26
- QuizGenerator/premade_questions/cst334/math_questions.py +42 -42
- QuizGenerator/premade_questions/cst334/memory_questions.py +124 -124
- QuizGenerator/premade_questions/cst334/persistence_questions.py +48 -48
- QuizGenerator/premade_questions/cst334/process.py +38 -38
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_calculation.py +45 -45
- QuizGenerator/premade_questions/cst463/gradient_descent/gradient_descent_questions.py +34 -34
- QuizGenerator/premade_questions/cst463/gradient_descent/loss_calculations.py +53 -53
- QuizGenerator/premade_questions/cst463/gradient_descent/misc.py +2 -2
- QuizGenerator/premade_questions/cst463/math_and_data/matrix_questions.py +65 -65
- QuizGenerator/premade_questions/cst463/math_and_data/vector_questions.py +39 -39
- QuizGenerator/premade_questions/cst463/models/attention.py +36 -36
- QuizGenerator/premade_questions/cst463/models/cnns.py +26 -26
- QuizGenerator/premade_questions/cst463/models/rnns.py +36 -36
- QuizGenerator/premade_questions/cst463/models/text.py +32 -32
- QuizGenerator/premade_questions/cst463/models/weight_counting.py +15 -15
- QuizGenerator/premade_questions/cst463/neural-network-basics/neural_network_questions.py +124 -124
- QuizGenerator/premade_questions/cst463/tensorflow-intro/tensorflow_questions.py +161 -161
- QuizGenerator/question.py +41 -41
- QuizGenerator/quiz.py +7 -7
- QuizGenerator/typst_utils.py +2 -2
- {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/METADATA +1 -1
- {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/RECORD +30 -30
- {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/WHEEL +0 -0
- {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/entry_points.txt +0 -0
- {quizgenerator-0.6.2.dist-info → quizgenerator-0.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,7 +9,7 @@ import logging
|
|
|
9
9
|
import math
|
|
10
10
|
from typing import List, Optional
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
import QuizGenerator.contentast as ca
|
|
13
13
|
from QuizGenerator.question import Question, QuestionRegistry, RegenerableChoiceMixin
|
|
14
14
|
from QuizGenerator.mixins import TableQuestionMixin, BodyTemplatesMixin
|
|
15
15
|
|
|
@@ -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: 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")
|
|
43
|
+
self.Target.VA_BITS: ca.AnswerTypes.Int(self.num_bits_va, unit="bits"),
|
|
44
|
+
self.Target.OFFSET_BITS: ca.AnswerTypes.Int(self.num_bits_offset, unit="bits"),
|
|
45
|
+
self.Target.VPN_BITS: ca.AnswerTypes.Int(self.num_bits_vpn, unit="bits")
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
# Select what kind of question we are going to be
|
|
@@ -71,9 +71,9 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
71
71
|
template_rows=table_data
|
|
72
72
|
)
|
|
73
73
|
|
|
74
|
-
body =
|
|
74
|
+
body = ca.Section()
|
|
75
75
|
body.add_element(
|
|
76
|
-
|
|
76
|
+
ca.Paragraph(
|
|
77
77
|
[
|
|
78
78
|
"Given the information in the below table, please complete the table as appropriate."
|
|
79
79
|
]
|
|
@@ -82,17 +82,17 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
82
82
|
body.add_element(table)
|
|
83
83
|
return body, answers
|
|
84
84
|
|
|
85
|
-
def get_body(self, **kwargs) ->
|
|
85
|
+
def get_body(self, **kwargs) -> ca.Section:
|
|
86
86
|
"""Build question body (backward compatible interface)."""
|
|
87
87
|
body, _ = self._get_body(**kwargs)
|
|
88
88
|
return body
|
|
89
89
|
|
|
90
90
|
def _get_explanation(self, **kwargs):
|
|
91
91
|
"""Build question explanation."""
|
|
92
|
-
explanation =
|
|
92
|
+
explanation = ca.Section()
|
|
93
93
|
|
|
94
94
|
explanation.add_element(
|
|
95
|
-
|
|
95
|
+
ca.Paragraph(
|
|
96
96
|
[
|
|
97
97
|
"Remember, when we are calculating the size of virtual address spaces, "
|
|
98
98
|
"the number of bits in the overall address space is equal to the number of bits in the VPN "
|
|
@@ -103,20 +103,20 @@ class VirtualAddressParts(MemoryQuestion, TableQuestionMixin):
|
|
|
103
103
|
)
|
|
104
104
|
|
|
105
105
|
explanation.add_element(
|
|
106
|
-
|
|
106
|
+
ca.Paragraph(
|
|
107
107
|
[
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
ca.Text(f"{self.num_bits_va}", emphasis=(self.blank_kind == self.Target.VA_BITS)),
|
|
109
|
+
ca.Text(" = "),
|
|
110
|
+
ca.Text(f"{self.num_bits_vpn}", emphasis=(self.blank_kind == self.Target.VPN_BITS)),
|
|
111
|
+
ca.Text(" + "),
|
|
112
|
+
ca.Text(f"{self.num_bits_offset}", emphasis=(self.blank_kind == self.Target.OFFSET_BITS))
|
|
113
113
|
]
|
|
114
114
|
)
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
return explanation, []
|
|
118
118
|
|
|
119
|
-
def get_explanation(self, **kwargs) ->
|
|
119
|
+
def get_explanation(self, **kwargs) -> ca.Section:
|
|
120
120
|
"""Build question explanation (backward compatible interface)."""
|
|
121
121
|
explanation, _ = self._get_explanation(**kwargs)
|
|
122
122
|
return explanation
|
|
@@ -245,16 +245,16 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
245
245
|
|
|
246
246
|
self.answers.update(
|
|
247
247
|
{
|
|
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),
|
|
248
|
+
f"answer__hit-{request_number}": ca.AnswerTypes.String(('hit' if was_hit else 'miss')),
|
|
249
|
+
f"answer__evicted-{request_number}": ca.AnswerTypes.String(('-' if evicted is None else f"{evicted}")),
|
|
250
|
+
f"answer__cache_state-{request_number}": ca.AnswerTypes.List(value=copy.copy(cache_state), order_matters=True),
|
|
251
251
|
}
|
|
252
252
|
)
|
|
253
253
|
|
|
254
254
|
self.hit_rate = 100 * number_of_hits / (self.num_requests)
|
|
255
255
|
self.answers.update(
|
|
256
256
|
{
|
|
257
|
-
"answer__hit_rate": AnswerTypes.Float(self.hit_rate,
|
|
257
|
+
"answer__hit_rate": ca.AnswerTypes.Float(self.hit_rate,
|
|
258
258
|
label=f"Hit rate, excluding non-capacity misses",
|
|
259
259
|
unit="%"
|
|
260
260
|
)
|
|
@@ -296,7 +296,7 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
296
296
|
answers.append(hit_rate_answer)
|
|
297
297
|
|
|
298
298
|
# Create hit rate answer block
|
|
299
|
-
hit_rate_block =
|
|
299
|
+
hit_rate_block = ca.AnswerBlock(hit_rate_answer)
|
|
300
300
|
|
|
301
301
|
# Use mixin to create complete body
|
|
302
302
|
intro_text = (
|
|
@@ -306,7 +306,7 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
306
306
|
"For the eviction column, please write either the number of the evicted page or simply a dash (e.g. \"-\")."
|
|
307
307
|
)
|
|
308
308
|
|
|
309
|
-
instructions =
|
|
309
|
+
instructions = ca.OnlyHtml([
|
|
310
310
|
"For the cache state, please enter the cache contents in the order suggested in class, "
|
|
311
311
|
"which means separated by commas with spaces (e.g. \"1, 2, 3\") "
|
|
312
312
|
"and with the left-most being the next to be evicted. "
|
|
@@ -317,19 +317,19 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
317
317
|
body.add_element(hit_rate_block)
|
|
318
318
|
return body, answers
|
|
319
319
|
|
|
320
|
-
def get_body(self, **kwargs) ->
|
|
320
|
+
def get_body(self, **kwargs) -> ca.Section:
|
|
321
321
|
"""Build question body (backward compatible interface)."""
|
|
322
322
|
body, _ = self._get_body(**kwargs)
|
|
323
323
|
return body
|
|
324
324
|
|
|
325
325
|
def _get_explanation(self, **kwargs):
|
|
326
326
|
"""Build question explanation."""
|
|
327
|
-
explanation =
|
|
327
|
+
explanation = ca.Section()
|
|
328
328
|
|
|
329
|
-
explanation.add_element(
|
|
329
|
+
explanation.add_element(ca.Paragraph(["The full caching table can be seen below."]))
|
|
330
330
|
|
|
331
331
|
explanation.add_element(
|
|
332
|
-
|
|
332
|
+
ca.Table(
|
|
333
333
|
headers=["Page", "Hit/Miss", "Evicted", "Cache State"],
|
|
334
334
|
data=[
|
|
335
335
|
[
|
|
@@ -344,7 +344,7 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
344
344
|
)
|
|
345
345
|
|
|
346
346
|
explanation.add_element(
|
|
347
|
-
|
|
347
|
+
ca.Paragraph(
|
|
348
348
|
[
|
|
349
349
|
"To calculate the hit rate we calculate the percentage of requests "
|
|
350
350
|
"that were cache hits out of the total number of requests. "
|
|
@@ -356,7 +356,7 @@ class CachingQuestion(MemoryQuestion, RegenerableChoiceMixin, TableQuestionMixin
|
|
|
356
356
|
|
|
357
357
|
return explanation, []
|
|
358
358
|
|
|
359
|
-
def get_explanation(self, **kwargs) ->
|
|
359
|
+
def get_explanation(self, **kwargs) -> ca.Section:
|
|
360
360
|
"""Build question explanation (backward compatible interface)."""
|
|
361
361
|
explanation, _ = self._get_explanation(**kwargs)
|
|
362
362
|
return explanation
|
|
@@ -393,12 +393,12 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
393
393
|
self.virtual_address = self.rng.randint(1, int(self.bounds / self.PROBABILITY_OF_VALID))
|
|
394
394
|
|
|
395
395
|
if self.virtual_address < self.bounds:
|
|
396
|
-
self.answers["answer"] = AnswerTypes.Hex(
|
|
396
|
+
self.answers["answer"] = ca.AnswerTypes.Hex(
|
|
397
397
|
self.base + self.virtual_address,
|
|
398
398
|
length=math.ceil(math.log2(self.base + self.virtual_address))
|
|
399
399
|
)
|
|
400
400
|
else:
|
|
401
|
-
self.answers["answer"] = AnswerTypes.String("INVALID")
|
|
401
|
+
self.answers["answer"] = ca.AnswerTypes.String("INVALID")
|
|
402
402
|
|
|
403
403
|
def _get_body(self):
|
|
404
404
|
"""Build question body and collect answers."""
|
|
@@ -428,17 +428,17 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
428
428
|
)
|
|
429
429
|
return body, answers
|
|
430
430
|
|
|
431
|
-
def get_body(self) ->
|
|
431
|
+
def get_body(self) -> ca.Section:
|
|
432
432
|
"""Build question body (backward compatible interface)."""
|
|
433
433
|
body, _ = self._get_body()
|
|
434
434
|
return body
|
|
435
435
|
|
|
436
436
|
def _get_explanation(self):
|
|
437
437
|
"""Build question explanation."""
|
|
438
|
-
explanation =
|
|
438
|
+
explanation = ca.Section()
|
|
439
439
|
|
|
440
440
|
explanation.add_element(
|
|
441
|
-
|
|
441
|
+
ca.Paragraph(
|
|
442
442
|
[
|
|
443
443
|
"There's two steps to figuring out base and bounds.",
|
|
444
444
|
"1. Are we within the bounds?\n",
|
|
@@ -449,7 +449,7 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
449
449
|
)
|
|
450
450
|
|
|
451
451
|
explanation.add_element(
|
|
452
|
-
|
|
452
|
+
ca.Paragraph(
|
|
453
453
|
[
|
|
454
454
|
f"Step 1: 0x{self.virtual_address:X} < 0x{self.bounds:X} "
|
|
455
455
|
f"--> {'***VALID***' if (self.virtual_address < self.bounds) else 'INVALID'}"
|
|
@@ -459,7 +459,7 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
459
459
|
|
|
460
460
|
if self.virtual_address < self.bounds:
|
|
461
461
|
explanation.add_element(
|
|
462
|
-
|
|
462
|
+
ca.Paragraph(
|
|
463
463
|
[
|
|
464
464
|
f"Step 2: Since the previous check passed, we calculate "
|
|
465
465
|
f"0x{self.base:X} + 0x{self.virtual_address:X} "
|
|
@@ -470,7 +470,7 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
470
470
|
)
|
|
471
471
|
else:
|
|
472
472
|
explanation.add_element(
|
|
473
|
-
|
|
473
|
+
ca.Paragraph(
|
|
474
474
|
[
|
|
475
475
|
f"Step 2: Since the previous check failed, we simply write ***INVALID***.",
|
|
476
476
|
"***If*** it had been valid, we would have calculated "
|
|
@@ -482,7 +482,7 @@ class BaseAndBounds(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin
|
|
|
482
482
|
|
|
483
483
|
return explanation, []
|
|
484
484
|
|
|
485
|
-
def get_explanation(self) ->
|
|
485
|
+
def get_explanation(self) -> ca.Section:
|
|
486
486
|
"""Build question explanation (backward compatible interface)."""
|
|
487
487
|
explanation, _ = self._get_explanation()
|
|
488
488
|
return explanation
|
|
@@ -579,15 +579,15 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
579
579
|
|
|
580
580
|
# Set answers based on whether it's in bounds or not
|
|
581
581
|
if self.__within_bounds(self.segment, self.offset, self.bounds[self.segment]):
|
|
582
|
-
self.answers["answer__physical_address"] = AnswerTypes.Binary(
|
|
582
|
+
self.answers["answer__physical_address"] = ca.AnswerTypes.Binary(
|
|
583
583
|
self.physical_address,
|
|
584
584
|
length=self.physical_bits,
|
|
585
585
|
label="Physical Address"
|
|
586
586
|
)
|
|
587
587
|
else:
|
|
588
|
-
self.answers["answer__physical_address"] = AnswerTypes.String("INVALID", label="Physical Address")
|
|
588
|
+
self.answers["answer__physical_address"] = ca.AnswerTypes.String("INVALID", label="Physical Address")
|
|
589
589
|
|
|
590
|
-
self.answers["answer__segment"] = AnswerTypes.String(self.segment, label="Segment name")
|
|
590
|
+
self.answers["answer__segment"] = ca.AnswerTypes.String(self.segment, label="Segment name")
|
|
591
591
|
|
|
592
592
|
def _get_body(self):
|
|
593
593
|
"""Build question body and collect answers."""
|
|
@@ -596,10 +596,10 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
596
596
|
self.answers["answer__physical_address"]
|
|
597
597
|
]
|
|
598
598
|
|
|
599
|
-
body =
|
|
599
|
+
body = ca.Section()
|
|
600
600
|
|
|
601
601
|
body.add_element(
|
|
602
|
-
|
|
602
|
+
ca.Paragraph(
|
|
603
603
|
[
|
|
604
604
|
f"Given a virtual address space of {self.virtual_bits}bits, "
|
|
605
605
|
f"and a physical address space of {self.physical_bits}bits, "
|
|
@@ -627,23 +627,23 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
627
627
|
body.add_element(segment_table)
|
|
628
628
|
|
|
629
629
|
body.add_element(
|
|
630
|
-
|
|
630
|
+
ca.AnswerBlock([
|
|
631
631
|
self.answers["answer__segment"],
|
|
632
632
|
self.answers["answer__physical_address"]
|
|
633
633
|
])
|
|
634
634
|
)
|
|
635
635
|
return body, answers
|
|
636
636
|
|
|
637
|
-
def get_body(self) ->
|
|
637
|
+
def get_body(self) -> ca.Section:
|
|
638
638
|
"""Build question body (backward compatible interface)."""
|
|
639
639
|
body, _ = self._get_body()
|
|
640
640
|
return body
|
|
641
641
|
|
|
642
642
|
def _get_explanation(self):
|
|
643
|
-
explanation =
|
|
643
|
+
explanation = ca.Section()
|
|
644
644
|
|
|
645
645
|
explanation.add_element(
|
|
646
|
-
|
|
646
|
+
ca.Paragraph(
|
|
647
647
|
[
|
|
648
648
|
"The core idea to keep in mind with segmentation is that you should always check ",
|
|
649
649
|
"the first two bits of the virtual address to see what segment it is in and then go from there."
|
|
@@ -654,7 +654,7 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
654
654
|
)
|
|
655
655
|
|
|
656
656
|
explanation.add_element(
|
|
657
|
-
|
|
657
|
+
ca.Paragraph(
|
|
658
658
|
[
|
|
659
659
|
f"In this problem our virtual address, "
|
|
660
660
|
f"converted to binary and including padding, is 0b{self.virtual_address:0{self.virtual_bits}b}.",
|
|
@@ -667,7 +667,7 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
667
667
|
|
|
668
668
|
if self.segment == "unallocated":
|
|
669
669
|
explanation.add_element(
|
|
670
|
-
|
|
670
|
+
ca.Paragraph(
|
|
671
671
|
[
|
|
672
672
|
"Since this is the unallocated segment there are no possible valid translations, so we enter ***INVALID***."
|
|
673
673
|
]
|
|
@@ -675,7 +675,7 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
675
675
|
)
|
|
676
676
|
else:
|
|
677
677
|
explanation.add_element(
|
|
678
|
-
|
|
678
|
+
ca.Paragraph(
|
|
679
679
|
[
|
|
680
680
|
f"Since we are in the {self.segment} segment, "
|
|
681
681
|
f"we see from our table that our bounds are {self.bounds[self.segment]}. "
|
|
@@ -690,7 +690,7 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
690
690
|
if not self.__within_bounds(self.segment, self.offset, self.bounds[self.segment]):
|
|
691
691
|
# then we are outside of bounds
|
|
692
692
|
explanation.add_element(
|
|
693
|
-
|
|
693
|
+
ca.Paragraph(
|
|
694
694
|
[
|
|
695
695
|
"We can therefore see that we are outside of bounds so we should put ***INVALID***.",
|
|
696
696
|
"If we <i>were</i> requesting a valid memory location we could use the below steps to do so."
|
|
@@ -700,7 +700,7 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
700
700
|
)
|
|
701
701
|
else:
|
|
702
702
|
explanation.add_element(
|
|
703
|
-
|
|
703
|
+
ca.Paragraph(
|
|
704
704
|
[
|
|
705
705
|
"We are therefore in bounds so we can calculate our physical address, as we do below."
|
|
706
706
|
]
|
|
@@ -708,7 +708,7 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
708
708
|
)
|
|
709
709
|
|
|
710
710
|
explanation.add_element(
|
|
711
|
-
|
|
711
|
+
ca.Paragraph(
|
|
712
712
|
[
|
|
713
713
|
"To find the physical address we use the formula:",
|
|
714
714
|
"<code>physical_address = base(segment) + offset</code>",
|
|
@@ -720,14 +720,14 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
720
720
|
)
|
|
721
721
|
|
|
722
722
|
explanation.add_element(
|
|
723
|
-
|
|
723
|
+
ca.Paragraph(
|
|
724
724
|
[
|
|
725
725
|
"Lining this up for ease we can do this calculation as:"
|
|
726
726
|
]
|
|
727
727
|
)
|
|
728
728
|
)
|
|
729
729
|
explanation.add_element(
|
|
730
|
-
|
|
730
|
+
ca.Code(
|
|
731
731
|
f" 0b{self.base[self.segment]:0{self.physical_bits}b}\n"
|
|
732
732
|
f"<u>+ 0b{self.offset:0{self.physical_bits}b}</u>\n"
|
|
733
733
|
f" 0b{self.physical_address:0{self.physical_bits}b}\n"
|
|
@@ -736,7 +736,7 @@ class Segmentation(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin)
|
|
|
736
736
|
|
|
737
737
|
return explanation, []
|
|
738
738
|
|
|
739
|
-
def get_explanation(self) ->
|
|
739
|
+
def get_explanation(self) -> ca.Section:
|
|
740
740
|
"""Build question explanation (backward compatible interface)."""
|
|
741
741
|
explanation, _ = self._get_explanation()
|
|
742
742
|
return explanation
|
|
@@ -812,27 +812,27 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
812
812
|
|
|
813
813
|
self.answers.update(
|
|
814
814
|
{
|
|
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"),
|
|
815
|
+
"answer__vpn": ca.AnswerTypes.Binary(self.vpn, length=self.num_bits_vpn, label="VPN"),
|
|
816
|
+
"answer__offset": ca.AnswerTypes.Binary(self.offset, length=self.num_bits_offset, label="Offset"),
|
|
817
|
+
"answer__pte": ca.AnswerTypes.Binary(self.pte, length=(self.num_bits_pfn + 1), label="PTE"),
|
|
818
818
|
}
|
|
819
819
|
)
|
|
820
820
|
|
|
821
821
|
if self.is_valid:
|
|
822
822
|
self.answers.update(
|
|
823
823
|
{
|
|
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"
|
|
824
|
+
"answer__is_valid": ca.AnswerTypes.String("VALID", label="VALID or INVALID?"),
|
|
825
|
+
"answer__pfn": ca.AnswerTypes.Binary(self.pfn, length=self.num_bits_pfn, label="PFN"),
|
|
826
|
+
"answer__physical_address": ca.AnswerTypes.Binary(self.physical_address, length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
827
827
|
),
|
|
828
828
|
}
|
|
829
829
|
)
|
|
830
830
|
else:
|
|
831
831
|
self.answers.update(
|
|
832
832
|
{
|
|
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"),
|
|
833
|
+
"answer__is_valid": ca.AnswerTypes.String("INVALID", label="VALID or INVALID?"),
|
|
834
|
+
"answer__pfn": ca.AnswerTypes.String("INVALID", label="PFN"),
|
|
835
|
+
"answer__physical_address": ca.AnswerTypes.String("INVALID", label="Physical Address"),
|
|
836
836
|
}
|
|
837
837
|
)
|
|
838
838
|
|
|
@@ -847,10 +847,10 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
847
847
|
self.answers["answer__physical_address"],
|
|
848
848
|
]
|
|
849
849
|
|
|
850
|
-
body =
|
|
850
|
+
body = ca.Section()
|
|
851
851
|
|
|
852
852
|
body.add_element(
|
|
853
|
-
|
|
853
|
+
ca.Paragraph(
|
|
854
854
|
[
|
|
855
855
|
"Given the below information please calculate the equivalent physical address of the given virtual address, filling out all steps along the way.",
|
|
856
856
|
"Remember, we typically have the MSB representing valid or invalid."
|
|
@@ -885,14 +885,14 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
885
885
|
value_matrix.append(["...", "..."])
|
|
886
886
|
|
|
887
887
|
body.add_element(
|
|
888
|
-
|
|
888
|
+
ca.Table(
|
|
889
889
|
headers=["VPN", "PTE"],
|
|
890
890
|
data=value_matrix
|
|
891
891
|
)
|
|
892
892
|
)
|
|
893
893
|
|
|
894
894
|
body.add_element(
|
|
895
|
-
|
|
895
|
+
ca.AnswerBlock([
|
|
896
896
|
self.answers["answer__vpn"],
|
|
897
897
|
self.answers["answer__offset"],
|
|
898
898
|
self.answers["answer__pte"],
|
|
@@ -904,17 +904,17 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
904
904
|
|
|
905
905
|
return body, answers
|
|
906
906
|
|
|
907
|
-
def get_body(self, *args, **kwargs) ->
|
|
907
|
+
def get_body(self, *args, **kwargs) -> ca.Section:
|
|
908
908
|
"""Build question body (backward compatible interface)."""
|
|
909
909
|
body, _ = self._get_body(*args, **kwargs)
|
|
910
910
|
return body
|
|
911
911
|
|
|
912
912
|
def _get_explanation(self, *args, **kwargs):
|
|
913
913
|
"""Build question explanation."""
|
|
914
|
-
explanation =
|
|
914
|
+
explanation = ca.Section()
|
|
915
915
|
|
|
916
916
|
explanation.add_element(
|
|
917
|
-
|
|
917
|
+
ca.Paragraph(
|
|
918
918
|
[
|
|
919
919
|
"The core idea of Paging is we want to break the virtual address into the VPN and the offset. "
|
|
920
920
|
"From here, we get the Page Table Entry corresponding to the VPN, and check the validity of the entry. "
|
|
@@ -924,7 +924,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
924
924
|
)
|
|
925
925
|
|
|
926
926
|
explanation.add_element(
|
|
927
|
-
|
|
927
|
+
ca.Paragraph(
|
|
928
928
|
[
|
|
929
929
|
"Don't forget to pad with the appropriate number of 0s (the appropriate number is the number of bits)!",
|
|
930
930
|
]
|
|
@@ -932,7 +932,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
932
932
|
)
|
|
933
933
|
|
|
934
934
|
explanation.add_element(
|
|
935
|
-
|
|
935
|
+
ca.Paragraph(
|
|
936
936
|
[
|
|
937
937
|
f"Virtual Address = VPN | offset",
|
|
938
938
|
f"<tt>0b{self.virtual_address:0{self.num_bits_vpn + self.num_bits_offset}b}</tt> "
|
|
@@ -942,7 +942,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
942
942
|
)
|
|
943
943
|
|
|
944
944
|
explanation.add_element(
|
|
945
|
-
|
|
945
|
+
ca.Paragraph(
|
|
946
946
|
[
|
|
947
947
|
"We next use our VPN to index into our page table and find the corresponding entry."
|
|
948
948
|
f"Our Page Table Entry is ",
|
|
@@ -954,7 +954,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
954
954
|
|
|
955
955
|
if self.is_valid:
|
|
956
956
|
explanation.add_element(
|
|
957
|
-
|
|
957
|
+
ca.Paragraph(
|
|
958
958
|
[
|
|
959
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**"
|
|
960
960
|
]
|
|
@@ -962,7 +962,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
962
962
|
)
|
|
963
963
|
else:
|
|
964
964
|
explanation.add_element(
|
|
965
|
-
|
|
965
|
+
ca.Paragraph(
|
|
966
966
|
[
|
|
967
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**.",
|
|
968
968
|
"Therefore, we just write \"INVALID\" as our answer.",
|
|
@@ -973,7 +973,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
973
973
|
)
|
|
974
974
|
|
|
975
975
|
explanation.add_element(
|
|
976
|
-
|
|
976
|
+
ca.Paragraph(
|
|
977
977
|
[
|
|
978
978
|
"Next, we convert our PTE to our PFN by removing our metadata. "
|
|
979
979
|
"In this case we're just removing the leading bit. We can do this by applying a binary mask.",
|
|
@@ -983,7 +983,7 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
983
983
|
)
|
|
984
984
|
)
|
|
985
985
|
explanation.add_element(
|
|
986
|
-
|
|
986
|
+
ca.Equation(
|
|
987
987
|
f"\\texttt{{{self.pfn:0{self.num_bits_pfn}b}}} "
|
|
988
988
|
f"= \\texttt{{0b{self.pte:0{self.num_bits_pfn + 1}b}}} "
|
|
989
989
|
f"\\& \\texttt{{0b{(2 ** self.num_bits_pfn) - 1:0{self.num_bits_pfn + 1}b}}}"
|
|
@@ -992,13 +992,13 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
992
992
|
|
|
993
993
|
explanation.add_elements(
|
|
994
994
|
[
|
|
995
|
-
|
|
995
|
+
ca.Paragraph(
|
|
996
996
|
[
|
|
997
997
|
"We then add combine our PFN and offset, "
|
|
998
998
|
"Physical Address = PFN | offset",
|
|
999
999
|
]
|
|
1000
1000
|
),
|
|
1001
|
-
|
|
1001
|
+
ca.Equation(
|
|
1002
1002
|
fr"{r'\mathbf{' if self.is_valid else ''}\mathtt{{0b{self.physical_address:0{self.num_bits_pfn + self.num_bits_offset}b}}}{r'}' if self.is_valid else ''} = \mathtt{{0b{self.pfn:0{self.num_bits_pfn}b}}} \mid \mathtt{{0b{self.offset:0{self.num_bits_offset}b}}}"
|
|
1003
1003
|
)
|
|
1004
1004
|
]
|
|
@@ -1006,17 +1006,17 @@ class Paging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplatesMixin):
|
|
|
1006
1006
|
|
|
1007
1007
|
explanation.add_elements(
|
|
1008
1008
|
[
|
|
1009
|
-
|
|
1010
|
-
|
|
1009
|
+
ca.Paragraph(["Note: Strictly speaking, this calculation is:", ]),
|
|
1010
|
+
ca.Equation(
|
|
1011
1011
|
fr"{r'\mathbf{' if self.is_valid else ''}\mathtt{{0b{self.physical_address:0{self.num_bits_pfn + self.num_bits_offset}b}}}{r'}' if self.is_valid else ''} = \mathtt{{0b{self.pfn:0{self.num_bits_pfn}b}{0:0{self.num_bits_offset}}}} + \mathtt{{0b{self.offset:0{self.num_bits_offset}b}}}"
|
|
1012
1012
|
),
|
|
1013
|
-
|
|
1013
|
+
ca.Paragraph(["But that's a lot of extra 0s, so I'm splitting them up for succinctness"])
|
|
1014
1014
|
]
|
|
1015
1015
|
)
|
|
1016
1016
|
|
|
1017
1017
|
return explanation, []
|
|
1018
1018
|
|
|
1019
|
-
def get_explanation(self, *args, **kwargs) ->
|
|
1019
|
+
def get_explanation(self, *args, **kwargs) -> ca.Section:
|
|
1020
1020
|
"""Build question explanation (backward compatible interface)."""
|
|
1021
1021
|
explanation, _ = self._get_explanation(*args, **kwargs)
|
|
1022
1022
|
return explanation
|
|
@@ -1172,19 +1172,19 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1172
1172
|
|
|
1173
1173
|
# Set up answers
|
|
1174
1174
|
self.answers.update({
|
|
1175
|
-
"answer__pdi": AnswerTypes.Binary(self.pdi, length=self.num_bits_pdi,
|
|
1175
|
+
"answer__pdi": ca.AnswerTypes.Binary(self.pdi, length=self.num_bits_pdi,
|
|
1176
1176
|
label="PDI (Page Directory Index)"),
|
|
1177
|
-
"answer__pti": AnswerTypes.Binary(self.pti, length=self.num_bits_pti,
|
|
1177
|
+
"answer__pti": ca.AnswerTypes.Binary(self.pti, length=self.num_bits_pti,
|
|
1178
1178
|
label="PTI (Page Table Index)"),
|
|
1179
|
-
"answer__offset": AnswerTypes.Binary(self.offset, length=self.num_bits_offset,
|
|
1179
|
+
"answer__offset": ca.AnswerTypes.Binary(self.offset, length=self.num_bits_offset,
|
|
1180
1180
|
label="Offset"),
|
|
1181
|
-
"answer__pd_entry": AnswerTypes.Binary(self.pd_entry, length=(self.num_bits_pfn + 1),
|
|
1181
|
+
"answer__pd_entry": ca.AnswerTypes.Binary(self.pd_entry, length=(self.num_bits_pfn + 1),
|
|
1182
1182
|
label="PD Entry (from Page Directory)"),
|
|
1183
1183
|
"answer__pt_number": (
|
|
1184
|
-
AnswerTypes.Binary(self.page_table_number, length=self.num_bits_pfn,
|
|
1184
|
+
ca.AnswerTypes.Binary(self.page_table_number, length=self.num_bits_pfn,
|
|
1185
1185
|
label="Page Table Number")
|
|
1186
1186
|
if self.pd_valid
|
|
1187
|
-
else AnswerTypes.String("INVALID", label="Page Table Number")
|
|
1187
|
+
else ca.AnswerTypes.String("INVALID", label="Page Table Number")
|
|
1188
1188
|
),
|
|
1189
1189
|
})
|
|
1190
1190
|
|
|
@@ -1192,29 +1192,29 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1192
1192
|
# (regardless of whether that PTE is valid or invalid)
|
|
1193
1193
|
if self.pd_valid:
|
|
1194
1194
|
self.answers.update({
|
|
1195
|
-
"answer__pte": AnswerTypes.Binary(self.pte, length=(self.num_bits_pfn + 1),
|
|
1195
|
+
"answer__pte": ca.AnswerTypes.Binary(self.pte, length=(self.num_bits_pfn + 1),
|
|
1196
1196
|
label="PTE (from Page Table)"),
|
|
1197
1197
|
})
|
|
1198
1198
|
else:
|
|
1199
1199
|
# If PD is invalid, student can't look up the page table
|
|
1200
1200
|
# Accept both "INVALID" (for consistency) and "N/A" (for accuracy)
|
|
1201
1201
|
self.answers.update({
|
|
1202
|
-
"answer__pte": AnswerTypes.String(["INVALID", "N/A"], label="PTE (from Page Table)"),
|
|
1202
|
+
"answer__pte": ca.AnswerTypes.String(["INVALID", "N/A"], label="PTE (from Page Table)"),
|
|
1203
1203
|
})
|
|
1204
1204
|
|
|
1205
1205
|
# Validity, PFN, and Physical Address depend on BOTH levels being valid
|
|
1206
1206
|
if self.pd_valid and self.pt_valid:
|
|
1207
1207
|
self.answers.update({
|
|
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"
|
|
1208
|
+
"answer__is_valid": ca.AnswerTypes.String("VALID", label="VALID or INVALID?"),
|
|
1209
|
+
"answer__pfn": ca.AnswerTypes.Binary(self.pfn, length=self.num_bits_pfn, label="PFN"),
|
|
1210
|
+
"answer__physical_address": ca.AnswerTypes.Binary(self.physical_address, length=(self.num_bits_pfn + self.num_bits_offset), label="Physical Address"
|
|
1211
1211
|
),
|
|
1212
1212
|
})
|
|
1213
1213
|
else:
|
|
1214
1214
|
self.answers.update({
|
|
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"),
|
|
1215
|
+
"answer__is_valid": ca.AnswerTypes.String("INVALID", label="VALID or INVALID?"),
|
|
1216
|
+
"answer__pfn": ca.AnswerTypes.String("INVALID", label="PFN"),
|
|
1217
|
+
"answer__physical_address": ca.AnswerTypes.String("INVALID", label="Physical Address"),
|
|
1218
1218
|
})
|
|
1219
1219
|
|
|
1220
1220
|
def _get_body(self, *args, **kwargs):
|
|
@@ -1231,10 +1231,10 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1231
1231
|
self.answers["answer__physical_address"],
|
|
1232
1232
|
]
|
|
1233
1233
|
|
|
1234
|
-
body =
|
|
1234
|
+
body = ca.Section()
|
|
1235
1235
|
|
|
1236
1236
|
body.add_element(
|
|
1237
|
-
|
|
1237
|
+
ca.Paragraph([
|
|
1238
1238
|
"Given the below information please calculate the equivalent physical address of the given virtual address, filling out all steps along the way.",
|
|
1239
1239
|
"This problem uses **two-level (hierarchical) paging**.",
|
|
1240
1240
|
"Remember, we typically have the MSB representing valid or invalid."
|
|
@@ -1267,19 +1267,19 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1267
1267
|
|
|
1268
1268
|
# Use a simple text paragraph - the bold will come from markdown conversion
|
|
1269
1269
|
body.add_element(
|
|
1270
|
-
|
|
1270
|
+
ca.Paragraph([
|
|
1271
1271
|
"**Page Directory:**"
|
|
1272
1272
|
])
|
|
1273
1273
|
)
|
|
1274
1274
|
body.add_element(
|
|
1275
|
-
|
|
1275
|
+
ca.Table(
|
|
1276
1276
|
headers=["PDI", "PD Entry"],
|
|
1277
1277
|
data=pd_matrix
|
|
1278
1278
|
)
|
|
1279
1279
|
)
|
|
1280
1280
|
|
|
1281
1281
|
# Page Tables - use TableGroup for side-by-side display
|
|
1282
|
-
table_group =
|
|
1282
|
+
table_group = ca.TableGroup()
|
|
1283
1283
|
|
|
1284
1284
|
for pt_num in sorted(self.page_tables.keys()):
|
|
1285
1285
|
pt_matrix = []
|
|
@@ -1323,14 +1323,14 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1323
1323
|
|
|
1324
1324
|
table_group.add_table(
|
|
1325
1325
|
label=f"PTC 0b{pt_num:0{self.num_bits_pfn}b}:",
|
|
1326
|
-
table=
|
|
1326
|
+
table=ca.Table(headers=["PTI", "PTE"], data=pt_matrix)
|
|
1327
1327
|
)
|
|
1328
1328
|
|
|
1329
1329
|
body.add_element(table_group)
|
|
1330
1330
|
|
|
1331
1331
|
# Answer block
|
|
1332
1332
|
body.add_element(
|
|
1333
|
-
|
|
1333
|
+
ca.AnswerBlock([
|
|
1334
1334
|
self.answers["answer__pdi"],
|
|
1335
1335
|
self.answers["answer__pti"],
|
|
1336
1336
|
self.answers["answer__offset"],
|
|
@@ -1345,31 +1345,31 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1345
1345
|
|
|
1346
1346
|
return body, answers
|
|
1347
1347
|
|
|
1348
|
-
def get_body(self, *args, **kwargs) ->
|
|
1348
|
+
def get_body(self, *args, **kwargs) -> ca.Section:
|
|
1349
1349
|
"""Build question body (backward compatible interface)."""
|
|
1350
1350
|
body, _ = self._get_body(*args, **kwargs)
|
|
1351
1351
|
return body
|
|
1352
1352
|
|
|
1353
1353
|
def _get_explanation(self, *args, **kwargs):
|
|
1354
1354
|
"""Build question explanation."""
|
|
1355
|
-
explanation =
|
|
1355
|
+
explanation = ca.Section()
|
|
1356
1356
|
|
|
1357
1357
|
explanation.add_element(
|
|
1358
|
-
|
|
1358
|
+
ca.Paragraph([
|
|
1359
1359
|
"Two-level paging requires two lookups: first in the Page Directory, then in a Page Table.",
|
|
1360
1360
|
"The virtual address is split into three parts: PDI | PTI | Offset."
|
|
1361
1361
|
])
|
|
1362
1362
|
)
|
|
1363
1363
|
|
|
1364
1364
|
explanation.add_element(
|
|
1365
|
-
|
|
1365
|
+
ca.Paragraph([
|
|
1366
1366
|
"Don't forget to pad with the appropriate number of 0s!"
|
|
1367
1367
|
])
|
|
1368
1368
|
)
|
|
1369
1369
|
|
|
1370
1370
|
# Step 1: Extract PDI, PTI, Offset
|
|
1371
1371
|
explanation.add_element(
|
|
1372
|
-
|
|
1372
|
+
ca.Paragraph([
|
|
1373
1373
|
f"**Step 1: Extract components from Virtual Address**",
|
|
1374
1374
|
f"Virtual Address = PDI | PTI | Offset",
|
|
1375
1375
|
f"<tt>0b{self.virtual_address:0{self.num_bits_vpn + self.num_bits_offset}b}</tt> = "
|
|
@@ -1381,7 +1381,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1381
1381
|
|
|
1382
1382
|
# Step 2: Look up PD Entry
|
|
1383
1383
|
explanation.add_element(
|
|
1384
|
-
|
|
1384
|
+
ca.Paragraph([
|
|
1385
1385
|
f"**Step 2: Look up Page Directory Entry**",
|
|
1386
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>"
|
|
1387
1387
|
])
|
|
@@ -1390,7 +1390,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1390
1390
|
# Step 3: Check PD validity
|
|
1391
1391
|
pd_valid_bit = self.pd_entry // (2 ** self.num_bits_pfn)
|
|
1392
1392
|
explanation.add_element(
|
|
1393
|
-
|
|
1393
|
+
ca.Paragraph([
|
|
1394
1394
|
f"**Step 3: Check Page Directory Entry validity**",
|
|
1395
1395
|
f"The MSB (valid bit) is **{pd_valid_bit}**, so this PD Entry is **{'VALID' if self.pd_valid else 'INVALID'}**."
|
|
1396
1396
|
])
|
|
@@ -1398,7 +1398,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1398
1398
|
|
|
1399
1399
|
if not self.pd_valid:
|
|
1400
1400
|
explanation.add_element(
|
|
1401
|
-
|
|
1401
|
+
ca.Paragraph([
|
|
1402
1402
|
"Since the Page Directory Entry is invalid, the translation fails here.",
|
|
1403
1403
|
"We write **INVALID** for all remaining fields.",
|
|
1404
1404
|
"If it were valid, we would continue with the steps below.",
|
|
@@ -1408,14 +1408,14 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1408
1408
|
|
|
1409
1409
|
# Step 4: Extract PT number (if PD valid)
|
|
1410
1410
|
explanation.add_element(
|
|
1411
|
-
|
|
1411
|
+
ca.Paragraph([
|
|
1412
1412
|
f"**Step 4: Extract Page Table Number**",
|
|
1413
1413
|
"We remove the valid bit from the PD Entry to get the Page Table Number:"
|
|
1414
1414
|
])
|
|
1415
1415
|
)
|
|
1416
1416
|
|
|
1417
1417
|
explanation.add_element(
|
|
1418
|
-
|
|
1418
|
+
ca.Equation(
|
|
1419
1419
|
f"\\texttt{{{self.page_table_number:0{self.num_bits_pfn}b}}} = "
|
|
1420
1420
|
f"\\texttt{{0b{self.pd_entry:0{self.num_bits_pfn + 1}b}}} \\& "
|
|
1421
1421
|
f"\\texttt{{0b{(2 ** self.num_bits_pfn) - 1:0{self.num_bits_pfn + 1}b}}}"
|
|
@@ -1424,14 +1424,14 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1424
1424
|
|
|
1425
1425
|
if self.pd_valid:
|
|
1426
1426
|
explanation.add_element(
|
|
1427
|
-
|
|
1427
|
+
ca.Paragraph([
|
|
1428
1428
|
f"This tells us to use **Page Table #{self.page_table_number}**."
|
|
1429
1429
|
])
|
|
1430
1430
|
)
|
|
1431
1431
|
|
|
1432
1432
|
# Step 5: Look up PTE
|
|
1433
1433
|
explanation.add_element(
|
|
1434
|
-
|
|
1434
|
+
ca.Paragraph([
|
|
1435
1435
|
f"**Step 5: Look up Page Table Entry**",
|
|
1436
1436
|
f"Using PTI = <tt>0b{self.pti:0{self.num_bits_pti}b}</tt> in Page Table #{self.page_table_number}, "
|
|
1437
1437
|
f"we find PTE = <tt>0b{self.pte:0{self.num_bits_pfn + 1}b}</tt>"
|
|
@@ -1441,7 +1441,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1441
1441
|
# Step 6: Check PT validity
|
|
1442
1442
|
pt_valid_bit = self.pte // (2 ** self.num_bits_pfn)
|
|
1443
1443
|
explanation.add_element(
|
|
1444
|
-
|
|
1444
|
+
ca.Paragraph([
|
|
1445
1445
|
f"**Step 6: Check Page Table Entry validity**",
|
|
1446
1446
|
f"The MSB (valid bit) is **{pt_valid_bit}**, so this PTE is **{'VALID' if self.pt_valid else 'INVALID'}**."
|
|
1447
1447
|
])
|
|
@@ -1449,7 +1449,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1449
1449
|
|
|
1450
1450
|
if not self.pt_valid:
|
|
1451
1451
|
explanation.add_element(
|
|
1452
|
-
|
|
1452
|
+
ca.Paragraph([
|
|
1453
1453
|
"Since the Page Table Entry is invalid, the translation fails.",
|
|
1454
1454
|
"We write **INVALID** for PFN and Physical Address.",
|
|
1455
1455
|
"If it were valid, we would continue with the steps below.",
|
|
@@ -1459,14 +1459,14 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1459
1459
|
|
|
1460
1460
|
# Step 7: Extract PFN
|
|
1461
1461
|
explanation.add_element(
|
|
1462
|
-
|
|
1462
|
+
ca.Paragraph([
|
|
1463
1463
|
f"**Step 7: Extract PFN**",
|
|
1464
1464
|
"We remove the valid bit from the PTE to get the PFN:"
|
|
1465
1465
|
])
|
|
1466
1466
|
)
|
|
1467
1467
|
|
|
1468
1468
|
explanation.add_element(
|
|
1469
|
-
|
|
1469
|
+
ca.Equation(
|
|
1470
1470
|
f"\\texttt{{{self.pfn:0{self.num_bits_pfn}b}}} = "
|
|
1471
1471
|
f"\\texttt{{0b{self.pte:0{self.num_bits_pfn + 1}b}}} \\& "
|
|
1472
1472
|
f"\\texttt{{0b{(2 ** self.num_bits_pfn) - 1:0{self.num_bits_pfn + 1}b}}}"
|
|
@@ -1475,14 +1475,14 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1475
1475
|
|
|
1476
1476
|
# Step 8: Construct physical address
|
|
1477
1477
|
explanation.add_element(
|
|
1478
|
-
|
|
1478
|
+
ca.Paragraph([
|
|
1479
1479
|
f"**Step 8: Construct Physical Address**",
|
|
1480
1480
|
"Physical Address = PFN | Offset"
|
|
1481
1481
|
])
|
|
1482
1482
|
)
|
|
1483
1483
|
|
|
1484
1484
|
explanation.add_element(
|
|
1485
|
-
|
|
1485
|
+
ca.Equation(
|
|
1486
1486
|
fr"{r'\mathbf{' if self.is_valid else ''}\mathtt{{0b{self.physical_address:0{self.num_bits_pfn + self.num_bits_offset}b}}}{r'}' if self.is_valid else ''} = "
|
|
1487
1487
|
f"\\mathtt{{0b{self.pfn:0{self.num_bits_pfn}b}}} \\mid "
|
|
1488
1488
|
f"\\mathtt{{0b{self.offset:0{self.num_bits_offset}b}}}"
|
|
@@ -1491,7 +1491,7 @@ class HierarchicalPaging(MemoryAccessQuestion, TableQuestionMixin, BodyTemplates
|
|
|
1491
1491
|
|
|
1492
1492
|
return explanation, []
|
|
1493
1493
|
|
|
1494
|
-
def get_explanation(self, *args, **kwargs) ->
|
|
1494
|
+
def get_explanation(self, *args, **kwargs) -> ca.Section:
|
|
1495
1495
|
"""Build question explanation (backward compatible interface)."""
|
|
1496
1496
|
explanation, _ = self._get_explanation(*args, **kwargs)
|
|
1497
1497
|
return explanation
|