From 8dc92fb8e1d9c85a134c070e72c9503fb0819bf0 Mon Sep 17 00:00:00 2001 From: peteraaser Date: Tue, 2 Jun 2020 14:58:06 +0200 Subject: [PATCH] Remove MemToReg. Pretty sure MemToReg is a MIPS relic, it is redundant so long as all memory reads are put into registers. --- src/main/scala/Decoder.scala | 33 +++++------ src/main/scala/ToplevelSignals.scala | 2 - src/test/scala/RISCV/testRunner.scala | 14 +++-- theory1.org | 85 +++++++++++++++++++++++---- theory2.org | 1 - 5 files changed, 100 insertions(+), 35 deletions(-) diff --git a/src/main/scala/Decoder.scala b/src/main/scala/Decoder.scala index 758c250..c1d5273 100644 --- a/src/main/scala/Decoder.scala +++ b/src/main/scala/Decoder.scala @@ -46,13 +46,13 @@ class Decoder() extends Module { */ val opcodeMap: Array[(BitPat, List[UInt])] = Array( - // signal memToReg, regWrite, memRead, memWrite, branch, jump, branchType, Op1Select, Op2Select, ImmSelect, ALUOp - LW -> List(Y, Y, Y, N, N, N, branchType.DC, rs1, imm, ITYPE, ALUOps.ADD), + // signal regWrite, memRead, memWrite, branch, jump, branchType, Op1Select, Op2Select, ImmSelect, ALUOp + LW -> List(Y, Y, N, N, N, branchType.DC, rs1, imm, ITYPE, ALUOps.ADD), - SW -> List(N, N, N, Y, N, N, branchType.DC, rs1, imm, STYPE, ALUOps.ADD), + SW -> List(N, N, Y, N, N, branchType.DC, rs1, imm, STYPE, ALUOps.ADD), - ADD -> List(N, Y, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.ADD), - SUB -> List(N, Y, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.SUB), + ADD -> List(Y, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.ADD), + SUB -> List(Y, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.SUB), /** TODO: Fill in the blanks @@ -60,23 +60,22 @@ class Decoder() extends Module { ) - val NOP = List(N, N, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.DC) + val NOP = List(N, N, N, N, N, branchType.DC, rs1, rs2, ImmFormat.DC, ALUOps.DC) val decodedControlSignals = ListLookup( io.instruction.asUInt(), NOP, opcodeMap) - io.controlSignals.memToReg := decodedControlSignals(0) - io.controlSignals.regWrite := decodedControlSignals(1) - io.controlSignals.memRead := decodedControlSignals(2) - io.controlSignals.memWrite := decodedControlSignals(3) - io.controlSignals.branch := decodedControlSignals(4) - io.controlSignals.jump := decodedControlSignals(5) + io.controlSignals.regWrite := decodedControlSignals(0) + io.controlSignals.memRead := decodedControlSignals(1) + io.controlSignals.memWrite := decodedControlSignals(2) + io.controlSignals.branch := decodedControlSignals(3) + io.controlSignals.jump := decodedControlSignals(4) - io.branchType := decodedControlSignals(6) - io.op1Select := decodedControlSignals(7) - io.op2Select := decodedControlSignals(8) - io.immType := decodedControlSignals(9) - io.ALUop := decodedControlSignals(10) + io.branchType := decodedControlSignals(5) + io.op1Select := decodedControlSignals(6) + io.op2Select := decodedControlSignals(7) + io.immType := decodedControlSignals(8) + io.ALUop := decodedControlSignals(9) } diff --git a/src/main/scala/ToplevelSignals.scala b/src/main/scala/ToplevelSignals.scala index fe31613..f0dd271 100644 --- a/src/main/scala/ToplevelSignals.scala +++ b/src/main/scala/ToplevelSignals.scala @@ -39,7 +39,6 @@ object Instruction { class ControlSignals extends Bundle(){ - val memToReg = Bool() val regWrite = Bool() val memRead = Bool() val memWrite = Bool() @@ -51,7 +50,6 @@ class ControlSignals extends Bundle(){ object ControlSignals { def nop: ControlSignals = { val b = Wire(new ControlSignals) - b.memToReg := false.B b.regWrite := false.B b.memRead := false.B b.memWrite := false.B diff --git a/src/test/scala/RISCV/testRunner.scala b/src/test/scala/RISCV/testRunner.scala index e19dd69..e47761b 100644 --- a/src/test/scala/RISCV/testRunner.scala +++ b/src/test/scala/RISCV/testRunner.scala @@ -23,6 +23,7 @@ case class TestOptions( printVMtrace : Boolean, printVMfinal : Boolean, printMergedTrace : Boolean, + printBinary : Boolean, nopPadded : Boolean, breakPoints : List[Int], // Not implemented testName : String, @@ -35,7 +36,8 @@ case class TestResult( program : String, vmTrace : String, vmFinal : String, - sideBySide : String + sideBySide : String, + binary : String ) object TestRunner { @@ -59,6 +61,7 @@ object TestRunner { val vmTraceString = printVMtrace(trace, program) val vmFinalState = finalVM.regs.show val traceString = printLogSideBySide(trace, chiselTrace, program) + val binaryString = printBinary(binary) val regError = compareRegs(trace, chiselTrace) val memError = compareMem(trace, chiselTrace) @@ -69,7 +72,8 @@ object TestRunner { programString, vmTraceString, vmFinalState.toString, - traceString) + traceString, + binaryString) } testResults.left.foreach{ error => @@ -79,15 +83,16 @@ object TestRunner { testResults.map{ testResults => val successful = List(testResults.regError, testResults.memError).flatten.headOption.map(_ => false).getOrElse(true) if(successful) - say(s"${testOptions.testName} succesful") + sayGreen(s"${testOptions.testName} succesful") else - say(s"${testOptions.testName} failed") + sayRed(s"${testOptions.testName} failed") if(testOptions.printIfSuccessful && successful){ if(testOptions.printParsedProgram) say(testResults.program) if(testOptions.printVMtrace) say(testResults.vmTrace) if(testOptions.printVMfinal) say(testResults.vmFinal) if(testOptions.printMergedTrace) say(testResults.sideBySide) + if(testOptions.printBinary) say(testResults.binary) } else{ if(testOptions.printErrors){ @@ -98,6 +103,7 @@ object TestRunner { if(testOptions.printVMtrace) say(testResults.vmTrace) if(testOptions.printVMfinal) say(testResults.vmFinal) if(testOptions.printMergedTrace) say(testResults.sideBySide) + if(testOptions.printBinary) say(testResults.binary) } successful }.toOption.getOrElse(false) diff --git a/theory1.org b/theory1.org index 7a06ab5..3dceefc 100644 --- a/theory1.org +++ b/theory1.org @@ -6,12 +6,15 @@ when grading these questions, thus even with no implementation at all you should still be able to score 100% on the theory questions. - All questions can be answered in a few sentences. Remember that brevity is the - soul of wit, and also the key to getting a good score. + All questions can be answered in a few sentences. Remember that brevity is wit, + and also the key to getting a good score. + You should easily be able to fit your entire answer on a single screen. ** Question 1 - 2 points. + *2 points.* *** Part 1 +**** Part 1½ + *½ points.* When decoding the BNE branch instruction in the above assembly program #+begin_src asm bne x6, x2, "loop", @@ -19,13 +22,27 @@ In your design, what is the value of each of the control signals below? - + memToReg + regWrite + memRead + memWrite + branch + jump +**** Part 1¼ + *½ points.* + When decoding the LW instruction in the above assembly program + #+begin_src asm + jal x1, 0x10(x1) + #+end_src + + In your design, what is the value of each of the control signals below? + + + regWrite + + memRead + + memWrite + + branch + + jump + Keep in mind that your design and your implementation are separate entities, thus you should answer this question based on your ideal design, not your finished implementation. @@ -33,7 +50,6 @@ *** Part 2 During execution, at some arbitrary cycle the control signals are: - + memToReg = 0 + regWrite = 1 + memRead = 0 + memWrite = 0 @@ -48,7 +64,10 @@ implementation. ** Question 2 - 4 points. + *4 points.* + *NO PARTIAL CREDITS* + Since you can test your solution with the testing framework I will not offer any + points for a near correct solution to this problem. Reading the binary of a RISC-V program you get the following: @@ -77,11 +96,23 @@ #+end_src *Your answer should be in the form of a simple asm program.* - (hint 1: the original asm program had a label, you need to infer where that label was) - (hint 2: verify your conclusion by assembling your answer) + + hint 1: + the original asm program had a label, you need to infer where that label was + + + hint 2: + Verify your conclusion by assembling your answer. + To do this, make an asm program, place it with the rest of the tests and set + ~printBinary~ to ~true~ in ~singleTestOptions~ in ~Manifest.scala~ which will + print the full binary of your program. + As long as your program generates the same binary as the supplied your program + is correct. + ** Question 3 - 4 points. + *4 points.* + *NO PARTIAL CREDITS* + Since you can test your solution with the testing framework I will not offer any + points for a near correct solution to this problem. In order to load a large number LUI and ADDI are used. consider the following program @@ -94,5 +125,37 @@ #+end_src a) Which of these instructions will be split into ADDI LUI pairs? - b) Why do the two last instructions need to be handled differently from each other? - (hint: The parser and assembler in the test suite can help you answer this question) + b) Explain in 3 sentences or less *how* the two last ops are handled differently and *why*. + + + hint 1: + The parser and assembler in the test suite can help you answer the first part of + this question (a). + Create an asm file, put it with the rest of the tests and run it, setting the correct + test options in ~singleTestOptions~ defined in ~Manifest.scala~ and observe the output. + + + hint 2: + While it's probably easier to solve this problem using the internet, however you + can also figure out what is happening by browsing the assembler source code which + will hopefully give you a deeper insight into what is going on here. + + Look at ~Parser.scala~, specifically what happens when an ~li~ instruction is parsed. + When parsing an instruction the parser first attempts to apply the + ~singleInstruction~ rule, however this only succeeds if the immediate value + obeys certain restrictions (~nBits <= 12~), if not it fails. + + If the ~singleInstruction~ rule fails the parser then attempts to apply the + ~multipleInstructions~ rule instead which expands operations into a list of real ops. + When this happens the resulting operations are defined as the following: + #+begin_src scala + stringWs("li") ~> (reg <~ sep, (hex | int).map(_.splitHiLo(20))).mapN{ case(rd, (hi, lo)) => { + List( + ArithImm.add(rd, rd, lo), + LUI(rd, if(lo > 0) hi else hi+1), + )}}.map(_.widen[Op]), + #+end_src + This is quite a lot to unpack, but you can focus on the line where the ~LUI~ is constructed. + ~hi~ and ~lo~ are the results of ~splitHiLo~ which splits a 32 bit word into a 12 bit and a + 20 bit. + Try this for yourself on paper; what happens when ~lo~ ends up being a negative number? + What is the interplay between incrementing ~hi~ with 1 and adding a ~lo~ that is represented + as a negative value? diff --git a/theory2.org b/theory2.org index 8400979..fbb99e8 100644 --- a/theory2.org +++ b/theory2.org @@ -46,7 +46,6 @@ rs1: 4 || rs1: 4 || rs1: 1 rs2: 5 || rs2: 6 || rs2: 2 rd: 6 || rd: 4 || rd: 5 - memToReg = false || memToReg = false || memToReg = false regWrite = true || regWrite = false || regWrite = true memWrite = false || memWrite = false || memWrite = false branch = false || branch = true || branch = false