# WebRISC-V ###### tags: `RISC-V` `computer architure 2021` [TOC] ## A Web-based RISC-V pipeline simulation environment WebRISC-V is a web-based server-side RISC-V assembly language Pipeline Datapath simulation environment, which aims at easing students learning and instructors teaching experience. This web-based simulator permits the execution of RISC-V user-provided source code on a five-stage pipeline, while displaying the data of registers, memory and the internal state of the pipeline elements. One of the main advantages of WebRISC-V is the immediate availability in the web browser. ## Local installation You can simply use [WebRISC-V](https://webriscv.dii.unisi.it/) through the web. Or you can install WebRISC-V on a local server. The reference Installation has been done on the Linux distro Ubuntu 18.04 LTS (it works also on the Ubuntu WSL in Windows 10). ### Option 1: Apache - To install the web-server Apache with the PHP interpreter issue the following commands: ```bash= sudo apt -y update sudo apt -y install apache2 php libapache2-mod-php sudo apt -y install php-gmp sudo ufw allow "Apache" (ufw may fail if you do not have ufw... just ignore it) sudo systemctl status apache2 ``` - You should see something like: ```bash= apache2.service - The Apache HTTP Server Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled) Drop-In: /lib/systemd/system/apache2.service.d └─apache2-systemd.conf Active: active (running) since Tue 2019-05-15 10:12:29 UTC; 1min ago Main PID: 4867 (apache2) Tasks: 52 (limit: 1101) CGroup: /system.slice/apache2.service ├─4868 /usr/sbin/apache2 -k start ├─4869 /usr/sbin/apache2 -k start └─4870 /usr/sbin/apache2 -k start ``` - To install the WebRISC-V software: ```bash= wget https://github.com/Mariotti94/WebRISC-V/archive/refs/heads/master.zip -O WebRISC-V-master.zip unzip WebRISC-V-master.zip sudo rm /var/www/html/* sudo mv WebRISC-V-master/www/* /var/www/html/ ``` - In the browser now you can open WebRISC-V by entering URL `http://localhost`. ### Option 2: Docker - To install docker and docker-compose issue the following commands: ```bash= sudo apt-get update sudo apt-get install ca-certificates curl gnupg lsb-release curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io sudo systemctl start docker sudo systemctl enable docker sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose ``` - To install the WebRISC-V software: ```bash= wget https://github.com/Mariotti94/WebRISC-V/archive/refs/heads/master.zip -O WebRISC-V-master.zip unzip WebRISC-V-master.zip cd WebRISC-V-master sudo docker-compose up -d --build ``` - In the browser now you can open WebRISC-V by entering URL `http://localhost`. ### The final screen No matter what method you use, you should see the following screen at the end. ![](https://i.imgur.com/eZNdhPG.png) ## Features of WebRISC-V ### General structure - WebRISC-V has its back-end written in PHP and its front-end in HTML and JavaScript, and as such can be executed from the Web browser, providing the advantage of immediate accessibility to students without any installing. - This simulator includes most of the instructions of the 64-bit RISC-V base ISA (Instruction Set Architecture) module and its multiplication extension. To avoid slowdowns or crashing of the server, there are some limits below: - The program uploaded has no more than 256 instructions. - Execute in no more than 2000 clock cycles. - Use no more than 4KiB of data memory. - So, in case of eventual programming errors, such as infinite loops, the execution can stop anyway in short time. ### Loading code - WebRISC-V loads the RISC-V assembly via the **Load Program** button. ![](https://i.imgur.com/6Y7tT9H.png) - The user can load program using the following way: - Built-in examples. WebRISC-V provides several built-in examples to user. ![](https://i.imgur.com/pnBAZrF.png) - Modify your existing code. - Write RISC-V assembly code from scratch. - After loading the assembly instruction, the parser checks if there are unsupported/miswritten instructions or mis written labels. If there is an error, the simulation stops and the corresponding error message is displayed. ![](https://i.imgur.com/P7a4Xmo.png) - There is a mechanism to help users understand the instructions. - The list of supported instructions is always visible on the left side, besides the text box. ![](https://i.imgur.com/o0tnDAb.png) - If you click a instruction format in the list of supported instructions, it will tell you what the instruction you clicked does. For instance, if you want to understand what the `add` instruction does, you can simply click the `add` instruction in the list of supported instructions to do it. ![](https://i.imgur.com/NU7lhiX.png) ### Program execution - WebRISC-V assembly code execution can happen in two modes: - Executing all the code at once by clicking the **Execute ALL** button. - Step-by-step. The user can click the **Step Forward** button and **Step Back** button to observe the advancing of instruction in each stage of the pipeline datapath. ![](https://i.imgur.com/M5fcyCw.png) - The stage buffers have a specific color (pink, red, yellow, blue, green respectively for the Fetch, Decode, Execute, Memory and Write-Back stages). ![](https://i.imgur.com/I3XhnPJ.png) - The user can observe the internal state of the architectural elements by clicking on the desired pipeline elements. For example, the following figure shows the input and output values of the ALU unit when the `addi` instruction is executed. ![](https://i.imgur.com/qjlQjXl.png) - Each instruction in the loaded program get a color based on the stage where it is currently processed and displays the following items: - The instruction-memory address. - The instruction type (R, I, S, SB, UJ). - The binary representation. - The binary and decimal value of each field (OPCODE, RS1, RS2, RD, FUNCT, IMMEDIATE). - The current stage (Fetch, Decode, Execute, Memory, Write-Back) of the instruction in the pipeline. ![](https://i.imgur.com/PnVh5Ik.png) - The Data Memory tab shows the dword contents (64-bits) at a single address, at a specific address range or the content of the whole data memory. ![](https://i.imgur.com/yLVRY0k.png) - The Register tab shows the content of the registers. Their values are shown both in binary and decimal format. ![](https://i.imgur.com/eHptmbB.png) - The program name and the current clock cycle will be displayed on the left. ![](https://i.imgur.com/RSGXMha.png) :::info WebRISC-V implements little-endian addressing which is more convenient for extending the architecture from words of 32 bits, to double-words of 64 bits or quad-words of 128 bits. ::: ## Common issues in RISC-V pipeline We have already introduced the features of WebRISC-V. Later we will use some examples, such as the execution process of `lw` instruction in the pipeline datapath and some of the more common cases in pipeline datapath to help you familiarize yourself with the WebRISC-V simulator. ### 1. Load instruction in WebRISC-V pipeline datapath We store a integer 100 in the register `sp` from the beginning and then extract it by using `lw` instruction. - RISC-V code ```clike= addi sp, sp, -4 addi t0, zero, 100 sw t0, 0(sp) lw t0, 0(sp) add a0, zero, t0 li a7, 1 ecall # print what you extract from the memory addi sp, sp, 4 ``` #### Fetch stage 1. The instruction address of `lw t0, 0(sp)` is `0x0000000c`. 2. According to the format of load instruction of RISC-V, the binary presentation of the `lw t0, 0(sp)` is `0b00000000000000010010001010000011`. |imm[11:0]|rs1|010|rd|0000011| | ------- | - | - |- | ----- | |000000000000|00010|010|00101|0000011| |0|2(sp)|2|5(t0)|3| ![](https://i.imgur.com/NhEJhbd.png) #### Decode stage - The source register 1 is register `sp` which is `2` after decoding. Even if the source register 2 is not used in `lw`, it will still be decoded. - The data extracted from register `sp` is `5116`. - Because of the instruction `addi sp, sp, -4` in Write-Back stage, the MEM/WB.RegisterRD is 2 and the WB.DATA = 5116. The instruction `addi sp, sp, -4` and the instruction `lw t0, 0(sp)` are data dependency, RAW (Read After Write), since the destination register `sp` of instruction `addi` is the source register 1 of the instruction `lw`. The instruction `addi sp, sp, -4` will change the content of register `sp` first so that the instruction `lw t0, 0(sp)` can extract the correct content from the Registers component. There is no **data hazard**. ![](https://i.imgur.com/EahsUvN.png) - The Immediate Generator component generate a 64-bit immediate from the instruction[31:20]. ![](https://i.imgur.com/WXns29T.png) #### Execute stage - The green circled part is used to confirm whether the instruction currently executed in the Execute stage has a data hazard problem with the previous two instructions. The value calculated in the Memory or Write-Back stage needs to be sent to the Execute stage using the forwarding unit. - The purple circled part is used to determine the two operands of the ALU. The ALU Operand 1 from the value of the register `sp` and the ALU Operand 2 from the immediate extracted from the instruction. ![](https://i.imgur.com/1dbRkrC.png) - Then the ALU performs the addition operation. :::info There is a stall after instruction `lw t0, 0(sp)`. Since the instruction `lw t0, 0(sp)` and the instruction `add a0, zero, t0` have a problem which is data dependency and there is no forwarding path from Execute stage to Decode stage. It must be a `stall` inserted by WebRISC-V simulator or yourself before the instruction `add a0, zero, t0`. ![](https://i.imgur.com/BwL7DQk.png) ::: #### Memory stage - Use the MemWrite and the MemRead signals to determine which operation will be taken to access the data memory. ![](https://i.imgur.com/szm475Q.png) #### Write-Back stage - Use the MemToReg signal to determine the value, from the Memory stage or from the Execute stage, will be written into destination register. ![](https://i.imgur.com/OszzJpf.png) ### 2. Data Hazards --- - Data dependency between instructions. - Need to wait for previous instruction to complete it's data write. - For example, the sequence of instructions below have data hazards. Since the destination register of the instruction `addi t0, x0, 1` is the source register of both the instruction `xor t1, t0, x0` and `sub t2, t0, x0`. These two following instructions need to **wait** util the register `t0` is updated to the new value. ```c= addi t0, x0, 1 xor t1, t0, x0 # t0 depends on addi sub t2, t0, x0 # t0 depends on addi ``` - There are two ways to solve **data hazard**. #### Resolving Data Hazards - Strategy 1: **Stall.** Wait for the result to be available by freezing earlier pipeline stages. This method is simple, but the efficiency is relatively low, it will waste unnecessary clock cycles, resulting in high CPI (Cycles Per Instruction). - In WebRISC-V, you can remove the forwarding units, and watch the stall(-) added in the pipeline. ![](https://i.imgur.com/WAZWpOO.png)![](https://i.imgur.com/BPL4dxb.png) - The following figure displays the content of **Hazard Detection unit**. It must be two `stalls` between the instruction `addi t0, x0, 1` and `xor t1, t0, x0`. ![](https://i.imgur.com/aqQXDuk.png) - In this way, in the next clock cycle, instruction `addi t0, x0, 1` updates the value of the register `t0` in the Write-Back stage and then instruction `xor t1, t0, x0` in the Decode stage can fetch the correct value of the register `t0`. ![](https://i.imgur.com/YxQ19Jz.png) - Strategy 2: **Bypass.** Route data to the earlier pipeline stage as soon as it is calculated. This method is more expensive because it requires additional hardware, namely the forwarding units, but it can reduce the count of clock cycles and the CPI will be lower. ![](https://i.imgur.com/5VuzyBM.png) - **Hazard Detection unit** will detect hazard conditions and produces control signals accordingly. ```clike= // EX hazard if (EX/MEM.RegWrite and (EX/MEM.RegisterRD != 0) and (EX/MEM.RegisterRD == ID/EX.RegisterRS1)) CtrlMUX3 = 10 if (EX/MEM.RegWrite and (EX/MEM.RegisterRD != 0) and (EX/MEM.RegisterRD == ID/EX.RegisterRS2)) CtrlMUX4 = 10 // MEM hazard if (MEM/WB.RegWrite and (MEM/WB.RegisterRd != 0) and not (EX/MEM.RegWrite and (EX/MEM.RegisterRD != 0) and (EX/MEM.RegisterRD == ID/EX.RegisterRS1)) and (MEM/WB.RegisterRd == ID/EX.RegisterRS1)) CtrlMUX3 = 01 if (MEM/WB.RegWrite and (MEM/WB.RegisterRd != 0) and not (EX/MEM.RegWrite and (EX/MEM.RegisterRD != 0) and (EX/MEM.RegisterRD == ID/EX.RegisterRS2)) and (MEM/WB.RegisterRd == ID/EX.RegisterRS2)) CtrlMUX4 = 01 ``` ![](https://i.imgur.com/t9x0aMa.png) | | No Forwarding | Forwarding | | ----------- |:-------------:|:----------:| | stall cycle | 2 | 0 | ### 3. Data Hazard with Branch instuction --- - In WebRISC-V, Branch decisions are made in the ID stage, so it needs to fetch the right data at `ID` stage. - Below, we will discuss the situation of the data dependency with load-use data hazard and without, separate discussion into the pipeline with forwarding and without. #### Pipeline without Forwarding units 1. Data dependency ```clike= # Stall two instruction to fetch the right data from addi. addi t0, x0, 7 beq t0, x0, exit exit: ``` ![](https://i.imgur.com/oO7NVqZ.png) Since the source register `t0` of the instruction `beq t0, x0, exit` is the destination register of the instruction`addi t0, x0, 0`, there is a **Data hazard**. The instruction `beq t0, x0, exit` need to wait for previous instruction`addi t0, x0, 0` to write-back the value into destination register `t0` at `Write-Back` stage, so it can fetch the right data at `Decode` stage. ![](https://i.imgur.com/J8NpT0Y.png) 2. Data dependency with load-use data hazards. ```clike= # Stall two instructions to fetch the right data. addi sp, sp, -4 addi t0, x0, 7 sw t0, 0(sp) lw t0, 0(sp) beq t0, x0, exit exit: ``` ![](https://i.imgur.com/4DxXvLr.png) In this case, it performs same as the normal data dependency we mentioned above, the instruction `beq t0, x0, exit` need to wait for the instruction `lw t0, 0(sp)` step into `WB` stage by stall two cycles. #### Pipeline with Forwarding units 1. Data dependency ```clike= # Stall one instruction to fetch the right data from addi. addi t0, x0, 7 beq t0, x0, exit exit: ``` ![](https://i.imgur.com/J2CR1IO.png) In pipeline with forwarding units, the instruction `beq t0, x0, exit` can fetch the correct data after the instruction `addi t0, x0, 7` finishing the execution in the Execute stage. Since the forwarding units will pass the executed value to the Decode stage. ![](https://i.imgur.com/fyIqLIF.png) 2. Data dependency with load-use data hazards. ```clike= # Stall two instructions to fetch the right data. addi sp, sp, -4 addi t0, x0, 7 sw t0, 0(sp) lw t0, 0(sp) beq t0, x0, exit exit: ``` ![](https://i.imgur.com/u1JFJ4c.png) Even though the pipeline has forwarding unit, the instruciton `lw t0, 0(sp)` still need to fetch the data from the data memory in the Memory stage and value of the destination register will be updated in the Write-Back stage. Then the instruction `beq t0, x0, exit` can extract the correct value from its source register. ![](https://i.imgur.com/mgK9UeU.png) | | No Forwarding | No Forwarding | Forwarding | Forwarding | |:-----------:|:--------------:|:-------------:|:--------------:|:----------:| | ------ | data dependecy | load-use | data dependecy | load-use | | stall cycle | 2 | 2 | 1 | 2 | ### 4. Control Hazard --- #### Branch taken (Conditional jump) - The control hazard occurs when the pipeline makes wrong decisions on branch prediction. - An instruction followed by the branch instuction in the pipeline must be discarded. ```clike= addi t0, x0, 3 # t0 = 3 addi t1, x0, 3 # t1 = 3 beq t0, t1, exit xor t2, t0, t1 and t2, t0, t1 exit: sub t2, t0, t1 ``` ![](https://i.imgur.com/VL36Dnp.png) Since `t0 == t1`, the branch will take. ![](https://i.imgur.com/UaHnko4.png) Set `pc` to branch target address. ![](https://i.imgur.com/QBxeq8E.png) Meanwhile, the instruction `xor t2, t0, t1` in `IF` stage need to be flushed. ![](https://i.imgur.com/U4OSxIp.png) Since the branch decision is performed in the Decode stage, there is only one wasted clock cycle to flush the wrong instruction. ![](https://i.imgur.com/FcwBPPb.png) #### Jump and link (Unconditional jump) - The control hazard occurs when the pipeline decode the `jump` instruction. - There is a **control hazard** absolutely. ```clike= 0 j exit 4 addi t0, x0, 1 8 sub t1, t0, x0 12 exit: xori t0, x0, 1 ``` ![](https://i.imgur.com/1EOvmO3.png) The instruction `j exit` will be decoded in the Decode stage, so it already fetch the next instruction `addi t0, x0, 1` in the Fetch stage. ![](https://i.imgur.com/EqHB16r.png) After `jump` has been decoded, the existed instruction in the Fetch stage need to be flushed and fetch the next instruction `xori t0, x0, 1` by using the correct target address. ![](https://i.imgur.com/Io0zpRf.png) #### Strange part - The label will translate to pc-relative address while using the **conditional** and **unconditional jump** instruction. - For the following example, label `<exit>` will translate to `24` in 64-bit risc-v simulator. (An instruction is `64-bit` long.) ( so `8 * 3 = 24`) - We can see the true translation in execution table. ![](https://i.imgur.com/5CshT5D.png) ![](https://i.imgur.com/RRgAzsf.png) - But, in the SCHEMA LAYOUT, we can see the translated value of label `exit` for instruction `beq` is `20`. (We guess that seems like pc-absolute address in 32-bit simulator.) ![](https://i.imgur.com/15Kekqg.png) ### 5. System call There are three system calls supported by WebRISC-V, print int, print string and read int. How to use these system calls? You need two argument registers, `a0` and `a7`, to determine which system call to be executed and then use the instruction `ecall` to complete it. It will use the console provided by WebRISC-V to display the results. - Print Int - For example, the following program wants to print an Integer 100. - The integer `1` should be placed into the register `a7` to determine that the system call executes "print int". - And then the value of the register `a0` should be an integer that you want to print out. ```clike= addi t0, x0, 100 addi a7, x0, 1 add a0, x0, t0 ecall ``` ![](https://i.imgur.com/8E6qS4b.png) - Print String - For example, the following program wants to print a string "Hello WebRISC-V". - You need to declare a label contains a string in the data section. - The string `4` should be placed into the register `a7` to determine that the system call executes "print string". - And then the value of the register `a0` should be a string that you want to print out. ```clike= .data str: .string "Hello WebRISC-V" .text addi a7, x0, 4 la a0, str ecall ``` ![](https://i.imgur.com/s9saj7r.png) :::info It will be a "\n"(new line) concat at the end of the output when using the system call "print int" and "print string" in the WebRISC-V simulator. ::: - Read Int - For example, the following program wants to read an integer then print it. - The integer `5` should be placed into the register `a7` to determine that the system call executes "read int". - When executing the program, it will be stuck at ecall in the `ID` stage. - You need to click `CONSOLE` and submit your input, and then the input integer will store in the register `a0`. - Print and check `a0` value, below is `a0 + 3`. ![](https://i.imgur.com/bUuYA3y.png) ```clike= .text addi a7, x0, 5 # read int syscall code ecall # Submit input and store integer in $a0 addi a0, a0, 3 # $a0 + 3 addi a7, x0, 1 # print int syscall code ecall ``` ## Example We give the following examples obtained from homework1 or from the quiz, and the results of performing on WebRISC-V. ### 1.Number of 1 Bits ([LeetCode191](https://leetcode.com/problems/number-of-1-bits/)) ```c= int number_of_1bits(uint32_t n){ int count = 0; while (n != 0){ n = n & (n - 1); count++; } return count; } ``` ```clike= #################################### # Number of 1 Bits(https://hackmd.io/jj61kCP_R-etsz4c7KTOSw) # Problem Definition: # Write a function that takes an unsigned integer # and returns the number of ‘1’ bits # it has (also known as the Hamming weight). # Because in WebRISC-V, it doesn't have 0b to present binary. #################################### .data argument: .word 11 str1: .string "Number of 1 bits = " .text main: la s0, argument # 11 = 0b1011 lw s0, 0(s0) la a0, str1 addi a7, x0, 4 ecall # "Number of 1 bits = " mv t0, x0 # count = 0 loop: beq s0, x0, done # if(n==0) goto done addi s1, s0, -1 # t2 = argument-1 and s0, s0, s1 # n = n&(n-1) addi t0, t0, 1 # count++ j loop done: mv a0, t0 addi a7, x0, 1 ecall ``` ![](https://i.imgur.com/i8eDg7z.png) ### 2.MoveZeroes ([LeetCode283](https://leetcode.com/problems/move-zeroes/)) ```c= void moveZeroes(int nums[], int numsSize){ int index=0; for (int i = 0; i < numsSize; ++i){ if (nums[i]) nums[index++] = nums[i]; } while (index < numsSize) nums[index++] = 0; } ``` ```clike= #################################### # This program serves as a MoveZeroes(https://hackmd.io/@CRbocq-NR46M5LnNvliy7g/rJb57RKNF) # Given an integer array nums, move all 0’s to the end of it # while maintaining the relative order of the non-zero elements. #################################### .data nums: .word 0, 1, 0, 3, 12 numsSize: .word 5 before: .string "Before move ones = " after: .string "After move ones = " .text main: la s0, nums # s0 = nums[0] la s1, numsSize lw s1, 0(s1) # s1 = numsSize la a0, before # print string "before" addi a7, x0, 4 ecall jal print # print nums[] before move jal moveZeroes # moveZeroes la a0, after # print .string addi a7, x0, 4 ecall jal print # print nums[] after move j final_exit moveZeroes: addi a1, zero, 0 # a1: index = 0 addi t0, zero, -1 # t0: i = -1 Loop: addi t0, t0, 1 # i += 1 bge t0, s1, While # i >= len -> While: slli t1, t0, 2 # t1: i << 2 add t1, t1, s0 # t1 = nums[i] lw t1, 0(t1) beq t1, x0, Loop # nums[i] == 1 -> Loop: slli t2, a1, 2 # else, t2: index << 2 add t2, t2, s0 # t2 = nums[index] sw t1, 0(t2) # nums[index] = nums[i] addi a1, a1, 1 # index += 1 j Loop While: bge a1, s1, Exit # index >= numsSize : Exit slli t2, a1, 2 # t2: index << 2 add t2, t2, s0 # t2: nums[index] sw x0, 0(t2) # nums[index] = 0 addi a1, a1, 1 # index += 1 j While Exit: jr ra print: addi t0, x0, 0 # i = 0 loopi: bge t0, s1, end # i >= numsSize, end slli t1, t0, 2 # t1: i << 2 add t1, t1, s0 lw a0, 0(t1) addi a7, x0, 1 # print int ecall addi t0, t0, 1 # i += 1 j loopi end: jr ra final_exit: ``` ![](https://i.imgur.com/ANvCYK7.png) ### 3.Power of Three ([LeetCode326](https://leetcode.com/problems/power-of-three/)) ```c= #include <stdio.h> #include <stdbool.h> bool isPowerOfThree(int n){ if(n==0) return false; if(n==1) return true; while(n%3 == 0){ if(n == 3) return true; n=n/3; } return false; } int main() { int n = 27; bool x = isPowerOfThree(n); printf("%s\n", x ? "true" : "false"); return 0; } ``` ```clike= #################################### # LeetCode: 326.Power of Three (https://hackmd.io/@DAuS-TwtQl27wzt5g4o1NQ/BJWdNrlIY) # Given an integer n, # return true if it is a power of three. # Otherwise, return false. #################################### .data argument: .word 27 str1: .string "true" str2: .string "false" .text main: la a0, argument lw a0, 0(a0) # load the argument from static data jal isPowerOfThree # Jump to 'isPowerOfThree' and link j Exit isPowerOfThree: addi t0, x0, 1 # t0 = 1 beq a0, x0, else # if a0 == 0, go to 'else' beq a0, t0, then # if a0 == 1, go to 'then' addi t0, x0, 3 # t0 = 3 rem t1, a0, t0 # t1 = a0 % 3 bne t1, x0, else # if t1 != 0, go to 'else' j loop # jump to 'loop' loop: beq a0, t0, then # if a0 == 3, go to 'then' div a0, a0, t0 # a0 = a0 / t0 rem t1, a0, t0 # t1 = a0 % 3 bne t1, x0, else # if t1 != 0, go to 'else' j loop # jump to 'loop' then: la a0, str1 # load string addi a7, x0, 4 # print string ecall jr ra # go back to main else: la a0, str2 # load string addi a7, x0, 4 # print string ecall jr ra # go back to main Exit: ``` ![](https://i.imgur.com/7gfNnlq.png) ### 4.Single Number ([LeetCode136](https://leetcode.com/problems/single-number/)) ```c= #include <stdio.h> int main(){ int nums[9]={12,22,9,20,22,9,11,11,20}; int ans = 0; for(int i = 0 ; i < 9;++i){ ans ^= nums[i]; } printf("The only single number is: %d",ans); } ``` ```clike= #################################### # LeetCode 136. Single Number (https://hackmd.io/@F7GGdY_NSxe2XlypzW57AA/ryzCosRHK) # Given a non-empty array of integers nums, # every element appears twice except for one. Find that single one. # You must implement a solution with a linear runtime complexity # and use only constant extra space. #################################### .data nums: .word 12, 22, 9, 20, 22, 9, 11, 11, 20 len: .word 9 .text main: la s0, nums # s0 = nums base address la s1, len # s1 = len lw s1, 0(s1) addi a1, x0, 0 # a1 = ans addi t0, x0, 0 # for the loop time counting loop: bge t0, s1, print # execute 9 times slli t1, t0, 2 add t1, t1, s0 lw t1, 0(t1) # t1 = nums[i],every time we need to record 'nums' array value xor a1, a1, t1 # xor all num[i] addi t0, t0, 1 # array offset j loop print: # print answer mv a0, a1 addi a7, x0, 1 ecall ``` ![](https://i.imgur.com/cPsyLY7.png) ### 5. arch2021-quiz5-problem-D ```c= int price[6] = {7, 5, 8, 10, 15, 7}; int maximum = 10, c = 3, t = 5; for (int i = 0; i < 6; i++) { if (price[i] > maximum) maximum = price[i]; } int total_cost = maximum + c + t; ``` ```clike= #################################### # This program is arch2021-quiz5-problem-D # Find the maximum value from price array #=================================== # a1 = 6 - length of price # a2 = 3 - c # a3 = 5 - t # a4 = 10 - maximum # s0 = - address of price[0] #################################### .data price: .word 7, 5, 8, 10, 15, 7 len: .word 6 maximum: .word 10 c: .word 3 t: .word 5 .text main: la a1, len lw a1, 0(a1) # a1 = len of price la a2, c lw a2, 0(a2) # a2 = c la a3, t lw a3, 0(a3) # a3 = t la a4, maximum lw a4, 0(a4) # a4 = maximum la s0, price start: addi t0, x0, 0 # t0: i = 0 loop: slli t1, t0, 2 # i << 2 add t1, t1, s0 # t1 = price[t0] lw t1, 0(t1) bge a4, t1, skip # if (maximum >= price[i]):skip mv a4, t1 # else maximum = price[i]; ori a4, a4, 0 skip: addi t0, t0, 1 # i++ blt t0, a1, loop # i < len(price) add a4, a4, a2 # maximum + c add a4, a4, a3 # maximum + t add a0, a4, x0 # print total_cost addi a7, x0, 1 ecall ``` ![](https://i.imgur.com/kYTzyns.png) #### Strange part When loading the array with some `0` value in, it will encounter a bug. ```clike= .data nums: .word 0, 1, 0, 3, 12 .text main: la s0, nums # s0 = nums[0] addi a1, x0, 5 # a1 = len(nums) addi t0, x0, 0 # t0 = i = 0 loopi: bge t0, a1, end # i >= len(nums), end slli t1, t0, 2 # t1: i << 2 add t1, t1, s0 lw a0, 0(t1) # load nums[i] to argument a0 addi a7, x0, 1 # print int ecall addi t0, t0, 1 # i += 1 j loopi end: ``` After executing the whole program, the value we expected to print on the console should be `0, 1, 0, 3, 12`. But in WebRISC-V, we guess that it seems to ignore the `0` value and load the next value which is not `0`. (The result show below.) ![](https://i.imgur.com/j2hcOBb.png) :::info ![](https://i.imgur.com/rUkvyoE.png) After fixing the bug, the program runs with the correct result. ![](https://i.imgur.com/GuERdpV.png) ::: ## Reference Paper: [WebRISC-V: a Web-Based Education-Oriented RISC-V Pipeline Simulation Environment](http://www.dii.unisi.it/~giorgi/papers/Giorgi19-wcae.pdf) Github: [WebRISC-V](https://github.com/Mariotti94/WebRISC-V)