Solutions
A
Consider that we plan to deploy a custom RISC-V processor for a space mission, which requires additional data protection in memory. We have decided to implement a Hamming code with even parity to safeguard our data. For this question, please refer to the parity table shown below.
Please note that in this context, bit 0 is considered the least significant bit (LSB).
See also: What Are Bit Flips And How Are Spacecraft Protected From Them?
Implement a RISC-V function called calc_parity
that takes a 4-byte input in a0
and calculates the parity of its bits. It should return 1 for odd parity or 0 for even parity. For example:
calc_parity
must adhere to the RISC-V calling convention.
- A01 = ?
srli a0, a0, 1
It is important to note that this is a logical shift, as using an arithmetic shift could potentially lead to an infinite loop if bit 31 is set.
- A02 = ?
andi a0, t1, 1
We only need to retain one bit for identifying the parity, so we directly performandi a0, t1, 1
and store it in register a0 as the function return value。
Implement store_nibble
, a RISC-V function that takes four bits of data in a0, encodes them into seven bits (as a byte with 0 in the most significant bit [MSB]), and stores the result at the memory address specified in a1. This function does not return any value. You may assume that calc_parity
has been correctly implemented and adheres to the calling convention, but do not assume any specific implementation details of calc_parity
. Also, you may assume that the most significant 28 bits of the argument in a0 are set to 0.
- A03 = ?
a0- A04 = ?
s7- A05 = ?
0(s1)
List all registers that need to be saved in the prologue and restored in the epilogue in order for store_nibble
to follow the calling convention. If there are none, write N/A
. __ A06 __
- A06 = ?
s0, s1, s7, ra
All of these registers are used in the question template and must, therefore, be saved and restored to follow the calling convention. (Whilera
is not strictly a saved register,store_nibble
is calling functions and, therefore, needs to savera
as well.) Any additional saved registers used also need to be saved and restored.
sp
is a special case; even though it is callee-saved, it is not usually saved and restored from the stack. Thus, it was ignored.
B
Suppose you are tasked with writing a file system in C. The struct file_item
represents either a file or a directory. The data
union can hold either the contents of a file (as a string) or an array of pointers to child file_items
. For the purposes of this question, assume that pointers are 4 bytes in length.
We set X
to the maximum value that does not increase the union size. What is the maximum strlen
of a string we can store in a single file? __ B01 __
- B01 = ?
63
The size of a union is determined by its largest element, disregarding alignment rules, which we will not consider here. In a 32-bit system, pointers are 32 bits (4 bytes).children
is an array of 16 pointers, occupying bytes. Therefore, we must ensure thatchar contents[X]
does not exceed 64 bytes. Given that, since 1 byte is defined as the size of achar
by default, setting X = 64 fulfills our goal. Consequently,strlen(contents)
would return 63 because it does not count the null terminator in the string length.
Write code to create files and directories. Ensure that your code still works even if the input strings are freed later on. Assume that the input strings will fit inside a file_item_data
union.
Your solution should be fully correct, without any memory leaks, and should not include any additional lines.
- B02 = ?
strcpy(new_file->data.contents, contents)
(or equivalent statement)- B03 = ?
new_dir->is_directory = true
(or equivalent statement)
C
Consider a 16-bit fixed-point system with 1 sign bit, 5 integer bits, and 10 fraction bits. The 10 fraction bits continue where the integer bits left off, representing , , … , . For example, the bit representation 0b0 01101 1010000000
represents .
In this question, we will compare this fixed-point system to a 16-bit floating-point system that adheres to all IEEE-754 conventions, including denorms, NaNs, etc., with a 5-bit exponent and an exponent bias of -15.
Write in hexadecimal using the 16-bit fixed-point system described above. __ C01 __
- C01 = ?
0xD980
The sign bit is 0b1, as the number is negative.
The integer bits are obtained by converting 22 to unsigned binary, giving us 0b10110.
The fractional bits are obtained by converting 0.375 to unsigned binary, resulting in 0b0.0110000000, with the fractional part being 0b0110000000.
In total, we have 0b1101100110000000, which is equivalent to 0xD980 in hexadecimal.
Write in hexadecimal using the 16-bit floating point system system described above. __ C02 __
- C02 = ?
0xCD98
The floating point system in this question has 16 bits total, including 1 sign bit and 5 exponent
bits. That leaves 10 bits for the mantissa.
First, write 22.375 in binary: 0b1 0110.011
Then, move the binary point to create a number of the form 0b1.MM MMMM MMMM × .
This gives us 0b1.01 1001 1000 × .
The mantissa bits can be read directly from this form: 0b01 1001 1000. Remember to drop the implicit 1 to the left of the binary point.
The exponent is −4, which we can convert to bias notation by subtracting the bias: . In unsigned 5-bit binary, this is 0b10011.
The sign bit is 0b1, since the number is negative.
In total, we have 0b1 10011 01 1001 1000, which is 0xCD98 in hex.
How many numbers in the range (including 16, excluding 64) can be represented by the fixed-point system described above? __ C03 __
- C03 = ?
16 in the fixed point system is 0b10000.0000000000. The largest representable number in the fixed point system is 0b11111.1111111111, which is , which is just under 32.
In between these two numbers, we can choose every bit except the most-significant integer bit to be 0 or 1. That gives us 4 integer bits and 10 fractional bits that could all be 0 or 1. Each bit pattern represents a different number, so we have representable numbers in total.
D
Suppose we have the following program.
For a RISC architecture (RV32 in particular), our RISC-V compiler generates the following assembly code.
Assume that RISC-V instructions are 4 bytes each. Assume data values are 4 bytes each.
How many bytes is the RISC-V program? __ D01 __
- D01 = ?
12
There are three instructions in this RISC-V program:blt
,sub
, andj
. Each instruction occupies 4 bytes in memory, so the total size of the program is bytes. Therefore, the answer for D01 is 12 bytes.
Assume a=7 and b=3. How many data bytes are loaded/stored in the RISC program? __ D02 __
- D02 = ?
0
For this RISC-V code snippet above, there are no explicit "loaded" or "stored" actions observed.
Suppose we have a simple integer vector-vector addition implementation below.
Complete the above code to make the RISC-V assembly function as described in the comment.
- D03 = ?
sw x5, 0(x6)
Based on the context provided, where registerx5
already holds the result of an addition, and the subsequent instructionaddi x6, x6, 4 # bump pointer
indicates thatx6
is the pointer to the address of arrayc
, it is appropriate to store the computation result fromx5
into the address held byx6
. Therefore, the instruction should besw x5, 0(x6)
.- D04 = ?
bne x7, x8, Loop
According to the adjacent comments on D04 and above, we can infer thatx7
represents the variablei
andx8
represents the variableN
in the for loop. Thus, whenx7
equalsx8
, the loop terminates. Therefore, the instruction used for branching and conditional checking isbne x7, x8, Loop
.
Next, consider a compiler that unrolls loops and reschedules instructions, assuming the loop operates on an even number of elements in the vectors. Shown below.
- D05 = ?
lw x8, -4(x1)
- D06 = ?
lw x9, -4(x3)
- D07 = ?
sw x5, 0(x6)
- D08 = ?
sw x10, 4(x6)
- D09 = ?
add x6, x6, 8
- D10 = ?
bne x7, x11, Loop