After reading the spec, I found a number of cases where our CPU was not complying with edge-cases of the MIPS I ISA.
We should write tests for these and include them in our test bench
I've categorised these by the type of instruction (ALU/Load-Store/Branch)
ALU
[x] There is an operator in Verilog for doing Arithmetic shifts (preserving the sign bit), however it appears to have the same operation as the logical shift operator unless the register output is casted with $signed() - i.e. no sign extension is performed. Write a test to check SRA and SRAV properly preserve the sign bit.
test/mips/sra/03_negative_shift.asm
[x] Instructions that perform equality comparisons (BGTZ, BGEZ, BLEZ, BLTZ, BLTZAL, BGEZAL) all operate on signed numbers. For example, if both numbers are unsigned 0x80000000 > 0x7FFFFFFF; however branch comparisons operate on signed numbers, so 0x80000000 < 0x7FFFFFFF as 0x80000000 has the sign bit set - thus the opposite branch action should be taken.
[x] Writes to the $zero register should be ignored.
[x] Check that all registers are reset to zero; you can do this by ORing every register together into v0.
Branch
[x] All link instructions (JAL, JALR, BLTZAL, BGEZAL) must write the link address to reg31, regardless of if the branch condition is met.
test/mips/jal/01_jal.asm
test/mips/jal/02_jal.asm
test/mips/bltzal/02_bltzal.asm
test/mips/bgezal/03_bgezal.asm
[x] Both J and JAL instructions perform a PC-region branch. This means that the address to jump to is computed from the immediate value in the instruction, as well as the upper bits of the PC. What will catch people out here is that the PC value used is that of the branch delay slot - not the jump instruction. Quoting from the MIPS ISA, This definition creates the boundary case where the branch instruction is in the last word of a 256 MB region and can therefore only branch to the following 256 MB region containing the branch delay slot. This will be quite a complex test and will require modifications to our RAM to support it (such as making our memory mapped modulo some value).
[x] Check that JR temporarily stores the address (in the register) to jump to before executing the branch delay slot. It should jump to this temporarily stored value instead of the updated register value in the branch delay slot (in other words any instruction in the branch delay slot should not modify the jump address, even if it writes to the same register)
test/mips/jr/01_jr.asm
[x] Check that all branch instructions with an immediate offset handle negative offset values
Load-store
[x] Check that, for all load and store instructions, the immediate offset is sign extended (thus works with negative offsets)
[x] Check that the LH and LB instructions sign extend the value that is loaded into the register
[x] Check that the LHU and LBU instructions don't sign extend the value that is loaded into the register
[x] Check that offsets greater 3 work for load/store instructions
[x] Check that SB and SH instructions do not overwrite the full word
[x] We currently enforce byte-enables when RAM writes occur, however we do not do this for reads. It'd be good to conditionally enforce this in a few test cases to see what happens.
iverilog
[x] Some times iverilog/vvp can fail without outputting a non-zero exit code (I think when an assertion is logged). Try to reproduce this and guard against the test cases passing if this happens. This is a risky test as everything could fail if someone has bad assertions in their CPU - is there any way to disable assertions while testing?
Extension testing
[ ] If we have time, we can generate random MIPS programs and run them through a reference simulator. Those that do not generate exceptions, we can choose to run in our testbench and the reference simulator to try to find bugs. Fuzz testing like this is normally a great way to find bugs that would not otherwise be found
[x] Check that after a series of instructions, no other edits to any of the other registers have been made
[ ] Check the Avalon interface is being complied with by making sure all signals are synchronous to the clock, other than waitrequest.
After reading the spec, I found a number of cases where our CPU was not complying with edge-cases of the MIPS I ISA.
We should write tests for these and include them in our test bench
I've categorised these by the type of instruction (ALU/Load-Store/Branch)
ALU
$signed()
- i.e. no sign extension is performed. Write a test to checkSRA
andSRAV
properly preserve the sign bit.test/mips/sra/03_negative_shift.asm
0x80000000 > 0x7FFFFFFF
; however branch comparisons operate on signed numbers, so0x80000000 < 0x7FFFFFFF
as 0x80000000 has the sign bit set - thus the opposite branch action should be taken.Branch
link
instructions (JAL,JALR, BLTZAL, BGEZAL) must write the link address to reg31, regardless of if the branch condition is met.test/mips/jal/01_jal.asm
test/mips/jal/02_jal.asm
test/mips/bltzal/02_bltzal.asm
test/mips/bgezal/03_bgezal.asm
J
andJAL
instructions perform aPC-region
branch. This means that the address to jump to is computed from the immediate value in the instruction, as well as the upper bits of the PC. What will catch people out here is that the PC value used is that of the branch delay slot - not the jump instruction. Quoting from the MIPS ISA,This definition creates the boundary case where the branch instruction is in the last word of a 256 MB region and can therefore only branch to the following 256 MB region containing the branch delay slot.
This will be quite a complex test and will require modifications to our RAM to support it (such as making our memory mapped modulo some value).JR
temporarily stores the address (in the register) to jump to before executing the branch delay slot. It should jump to this temporarily stored value instead of the updated register value in the branch delay slot (in other words any instruction in the branch delay slot should not modify the jump address, even if it writes to the same register)test/mips/jr/01_jr.asm
Load-store
LH
andLB
instructions sign extend the value that is loaded into the registerLHU
andLBU
instructions don't sign extend the value that is loaded into the registerSB
andSH
instructions do not overwrite the full wordiverilog
Extension testing