protected void Tick(RexBoard mBoard) { for (int i = 0; i < NUM_TICKS; i++) { mBoard.Tick(); } }
public override bool Mark(RexBoard mBoard) { Console.WriteLine("This might take a while..."); Initialise(mBoard); for (uint i = 0; i <= 0xffff; i++) { ResetBoard(mBoard); // Flip some switches mBoard.Parallel.Switches = i; // Give a nice progress indicator if (i % (0xffff / 100) == 0) { Console.Write($"\r{i / (0xffff / 100)}%"); } // Let the program run bool passed = RunSerialTestCase("", "", $"{i:D5}", "", mBoard); if (!passed) { return(false); } } Console.WriteLine(); return(true); }
public RexBoardForm(string[] args) { InitializeComponent(); //setup arguments mArgs = args; //Set up form contents ResetToolStripStatusLabel(); //Set up all REX and WRAMP hardware mRexBoard = rexWidget1.mBoard; //Load WRAMPmon into ROM Stream wmon = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(Resources.monitor_srec)); rexWidget1.LoadSrec(wmon); wmon.Close(); //Set up the worker thread mWorkerEnabler = new ManualResetEvent(true); // CPU begins in a running state, since the reset is finished mWorker = new Thread(new ThreadStart(Worker)); mRexBoard.SetTickEnabler(mWorkerEnabler); //Set up all forms mSubforms = new List <Form>(); mSerialForm1 = new BasicSerialPortForm(mRexBoard.Serial1, this); mSerialForm2 = new BasicSerialPortForm(mRexBoard.Serial2, this); mGpRegisterForm = new RegisterForm(mRexBoard.CPU.mGpRegisters, false); mSpRegisterForm = new RegisterForm(mRexBoard.CPU.mSpRegisters, true); mRamForm = new MemoryForm(mRexBoard.RAM); mRamForm.SetCpu(mRexBoard.CPU); mInterruptButtonForm = new PeripheralMemoryForm(mRexBoard.InterruptButton); mSerialConfigForm1 = new PeripheralMemoryForm(mRexBoard.Serial1); mSerialConfigForm2 = new PeripheralMemoryForm(mRexBoard.Serial2); mParallelConfigForm = new PeripheralMemoryForm(mRexBoard.Parallel); mTimerConfigForm = new PeripheralMemoryForm(mRexBoard.Timer); //Add all forms to the list of subforms mSubforms.Add(mSerialForm1); mSubforms.Add(mSerialForm2); mSubforms.Add(mGpRegisterForm); mSubforms.Add(mSpRegisterForm); mSubforms.Add(mRamForm); mSubforms.Add(mInterruptButtonForm); mSubforms.Add(mSerialConfigForm1); mSubforms.Add(mSerialConfigForm2); mSubforms.Add(mParallelConfigForm); mSubforms.Add(mTimerConfigForm); //Wire up event handlers foreach (Form f in mSubforms) { f.VisibleChanged += new EventHandler(SubForm_VisibleChanged); } //Set the GUI update timer going! updateTimer.Start(); }
protected bool Test(uint switchCombination, uint expected, RexBoard board) { board.Parallel.Switches = switchCombination; for (int i = 0; i < CLOCK_CYCLE_LIMIT; i++) { board.Tick(); } //HACK: If they've turned off Hex decoding, modify the expected value if ((board.Parallel.Control & 1u) == 0u) { uint[] bitPattern = { 0x772F, 0x5B5B, 0x7D7C, 0x3F5E, 0x666F, 0x392F, 0x0771, 0x7C7F, 0x2F06 }; uint leftExpected = (bitPattern[CountBits(switchCombination)] >> 8) & 0xFF; uint rightExpected = bitPattern[CountBits(switchCombination)] & 0xFF; if (board.Parallel.LeftSSDOut != leftExpected || board.Parallel.RightSSDOut != rightExpected) { mMessage += string.Format("Incorrect SSD segments lit (Switches {0:X2}).\r\nWarning: SSD Hex decoding is disabled; it is recommended that you turn this on instead of setting segments on/off manually.\r\n", board.Parallel.Switches); return(false); } } else { uint result = board.Parallel.SSD; if (result != expected) { mMessage += string.Format("SSD incorrect: was {0:X2}, expected {1:X2} (Switches {2:X2})\r\n", result, expected, board.Parallel.Switches); return(false); } } return(true); }
/// <summary> /// Wags the dog. /// </summary> /// <param name="board"></param> protected int Wag(RexBoard board, int count = 10000) { for (int i = 0; i < count; i++) { board.Tick(); } return(count); }
public RexBoardForm() { InitializeComponent(); //allow drop event this.AllowDrop = true; //Set up all REX and WRAMP hardware mRexBoard = rexWidget1.mBoard; //Load WRAMPmon into ROM Stream wmon = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(Resources.monitor_srec)); rexWidget1.LoadSrec(wmon); wmon.Close(); //Set up the worker thread mWorker = new Thread(new ThreadStart(Worker)); //Set up all forms mSubforms = new List <Form>(); mSerialForm1 = new BasicSerialPortForm(mRexBoard.Serial1); mSerialForm2 = new BasicSerialPortForm(mRexBoard.Serial2); mGpRegisterForm = new RegisterForm(mRexBoard.CPU.mGpRegisters, false); mSpRegisterForm = new RegisterForm(mRexBoard.CPU.mSpRegisters, true); mRamForm = new MemoryForm(mRexBoard.RAM); mRamForm.SetCpu(mRexBoard.CPU); mInterruptButtonForm = new PeripheralMemoryForm(mRexBoard.InterruptButton); mSerialConfigForm1 = new PeripheralMemoryForm(mRexBoard.Serial1); mSerialConfigForm2 = new PeripheralMemoryForm(mRexBoard.Serial2); mParallelConfigForm = new PeripheralMemoryForm(mRexBoard.Parallel); mTimerConfigForm = new PeripheralMemoryForm(mRexBoard.Timer); //Add all forms to the list of subforms mSubforms.Add(mSerialForm1); mSubforms.Add(mSerialForm2); mSubforms.Add(mGpRegisterForm); mSubforms.Add(mSpRegisterForm); mSubforms.Add(mRamForm); mSubforms.Add(mInterruptButtonForm); mSubforms.Add(mSerialConfigForm1); mSubforms.Add(mSerialConfigForm2); mSubforms.Add(mParallelConfigForm); mSubforms.Add(mTimerConfigForm); //Wire up event handlers foreach (Form f in mSubforms) { f.VisibleChanged += new EventHandler(SubForm_VisibleChanged); } //Set the GUI update timer going! updateTimer.Start(); }
/// <summary> /// Restore the state we set up in Initialise, plus some other stuff. /// </summary> protected void ResetBoard(RexBoard mBoard) { if (SupressReset) { return; } mBoard.CPU.PC = originalPC; mBoard.CPU.mGpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.GpRegister.sp] = 0x03b7f; //sensible value for the stack pointer mBoard.CPU.mGpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.GpRegister.ra] = 0xFFFFF; //when the CPU tries to jump here, the test is over. mBoard.CPU.mSpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.SpRegister.evec] = 0x80000; //when the CPU tries to jump here, the test is over (should jal to exit, which is syscall). }
public override bool Mark(RexBoard board) { Initialise(board); for (int c = 0; c < TEST_CASES.Length; c++) { if (!TryTestCase(board, TEST_CASES[c], false)) { return(false); } } return(true); }
protected override bool DoSubTest(RexBoard mBoard) { //Check that the SSDs are showing the right value if (mBoard.Parallel.SSD != 0x00) { mMessage += "Before pressing the user interrupt button, SSDs did not show '00'\r\n"; return(false); } //Press the user interrupt button 10 times, see where it gets you. for (int i = 1; i < 10; i++) { mBoard.InterruptButton.PressButton(); Wag(mBoard); if ((mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf) != i) { mMessage += string.Format("SSDs showed \"{0:X2}\" after pressing the user interrupt button {1} times. Expected SSD to show \"{1:D2}\"\r\n", mBoard.Parallel.SSD, i); return(false); } if (mBoard.InterruptButton.InterruptAck != 0) { mMessage += "User Interrupt Button was not acknowledged."; return(false); } } // Press the other buttons some, see what happens. for (int i = 10; i < 100; i++) { mBoard.Parallel.Buttons = (uint)(1 << (i % 3)); Wag(mBoard); mBoard.Parallel.Buttons = 0; Wag(mBoard); if ((mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf) != i) { mMessage += string.Format("SSDs showed \"{0:X2}\" after pressing the parallel buttons {1} times. Expected SSD to show \"{2:D2}\"\r\n", mBoard.Parallel.SSD, i - 10, i); return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } } return(true); }
protected List <uint> RunTestCase(RexBoard mBoard, int tickLimit) { List <uint> ssdValues = new List <uint>(); for (int i = 0; i < tickLimit; i++) { mBoard.Tick(); //Record SSD values at each interrupt if (mBoard.CPU.PC == mBoard.CPU.mSpRegisters[RegisterFile.SpRegister.evec] + 1) { uint currentSsdValue = (mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf); if (ssdValues.Count == 0 || ssdValues.Last() != currentSsdValue) { ssdValues.Add(currentSsdValue); } Console.Write("SSDs: {0:D2}\r", currentSsdValue); } } return(ssdValues); }
/// <summary> /// Executes a test case. /// </summary> /// <param name="sendSP1">String to send to the serial port SP1</param> /// <param name="sendSP2">String to send to the serial port SP2</param> /// <param name="expectSP1">String expected to be received from SP1</param> /// <param name="expectSP2">String expected to be received from SP2</param> /// <param name="board">The board to run the tests on.</param> /// <returns>True if the test passes.</returns> protected bool RunSerialTestCase(string sendSP1, string sendSP2, string expectSP1, string expectSP2, RexBoard board) { if (Verbose) { Console.WriteLine("Test Case: "); if (sendSP1.Length > 0) { Console.WriteLine("-Sending '{0}' to SP1", sendSP1); } if (sendSP2.Length > 0) { Console.WriteLine("-Sending '{0}' to SP2", sendSP2); } if (expectSP1.Length > 0) { Console.WriteLine("-Expecting '{0}' from SP1", expectSP1); } if (expectSP2.Length > 0) { Console.WriteLine("-Expecting '{0}' from SP2", expectSP2); } } // We hook into the SerialIO devices' event handlers to correctly receive characters mSP1RecvBuf = mSP2RecvBuf = ""; EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs> serialHandler1 = new EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs>(Serial1_SerialDataTransmitted); EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs> serialHandler2 = new EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs>(Serial2_SerialDataTransmitted); board.Serial1.SerialDataTransmitted += serialHandler1; board.Serial2.SerialDataTransmitted += serialHandler2; int clocks = CLOCK_CYCLE_LIMIT; //Transmit the data, and exit early if the received data happens sooner than we hoped string toSend1 = sendSP1; string toSend2 = sendSP2; while (clocks-- > 0) { board.Tick(); if (toSend1.Length != 0 && board.Serial1.SendAsync(toSend1[0])) { toSend1 = toSend1.Substring(1); } if (toSend2.Length != 0 && board.Serial2.SendAsync(toSend2[0])) { toSend2 = toSend2.Substring(1); } if (expectSP1.Length != 0 && mSP1RecvBuf == expectSP1) { break; } if (expectSP2.Length != 0 && mSP2RecvBuf == expectSP2) { break; } } board.Serial1.SerialDataTransmitted -= serialHandler1; board.Serial2.SerialDataTransmitted -= serialHandler2; //Make sure the correct message was received if (mSP1RecvBuf != expectSP1) { if (sendSP1.Length != 0) { mMessage += string.Format("Sent \"{0}\" to SP1\r\n", sendSP1); } if (sendSP2.Length != 0) { mMessage += string.Format("Sent \"{0}\" to SP2\r\n", sendSP2); } mMessage += string.Format("Received \"{0}\" from SP1, expected \"{1}\"\r\n", mSP1RecvBuf, expectSP1); return(false); } if (mSP2RecvBuf != expectSP2) { if (sendSP1.Length != 0) { mMessage += string.Format("Sent \"{0}\" to SP1\r\n", sendSP1); } if (sendSP2.Length != 0) { mMessage += string.Format("Sent \"{0}\" to SP2\r\n", sendSP2); } mMessage += string.Format("Received \"{0}\" from SP2, expected \"{1}\"\r\n", mSP2RecvBuf, expectSP2); return(false); } return(true); }
public RexWidget() { InitializeComponent(); mBoard = new RexBoard(); }
protected override bool DoSubTest(RexBoard mBoard) { //Ensure timer is set up right if (mBoard.Timer.Control != 3) { mMessage += string.Format("Timer control register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Control); return(false); } if (mBoard.Timer.Load != 2400) { mMessage += string.Format("Timer load register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Load); return(false); } //Test for 100 seconds //Sneaky speedup to 10 interrupts per second, to save time. mBoard.Timer.Count = 0; mBoard.Timer.Load = 240; List <uint> ssdValues = new List <uint>(); long ticksAtStart = mBoard.TickCounter; int tickLimit = (int)((6.25e6 * TEST_DURATION_SECONDS + ticksAtStart) / 10); for (int i = 0; i < tickLimit; i++) //while(mBoard.TickCounter < 4e6 * TEST_DURATION_SECONDS / 10) { mBoard.Tick(); //Record SSD values at each interrupt if (mBoard.CPU.PC == mBoard.CPU.mSpRegisters[RegisterFile.SpRegister.evec] + 1) { if (ssdValues.Count == 0 || ssdValues.Last() != (mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf)) { ssdValues.Add((mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf)); } Console.Write("{0:D2}%\r", (int)(100.0 * i / tickLimit)); } } //Check for the correct sequence bool correctSequence = true; if (ssdValues.Count == TEST_DURATION_SECONDS) { for (uint i = 0; i < TEST_DURATION_SECONDS; i++) { if (ssdValues[(int)i] != i) { correctSequence = false; break; } } } else { correctSequence = false; } if (!correctSequence) { mMessage += "Ran your program for 100 (simulated) seconds and observed:\r\n"; foreach (uint i in ssdValues) { mMessage += string.Format("{0:D2}, ", i); } mMessage += "\r\n\r\nExpected to see a sequence from \"00\" to \"99\" inclusive, in the correct order.\r\n"; return(false); } return(true); }
protected override bool DoSubTest(RexBoard mBoard) { //Ensure timer is set up right if (mBoard.Timer.Control != 2) { mMessage += string.Format("Timer control register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Control); return(false); } if (mBoard.Timer.Load != 24) { mMessage += string.Format("Timer load register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Load); return(false); } //Test for 100 seconds List <uint> ssdValues; //10 seconds of nothing Console.WriteLine("Running program..."); int tickLimit = (int)((6.25e6 * 10)); ssdValues = RunTestCase(mBoard, tickLimit); if (ssdValues.Count != 0) { mMessage += "After running the program for 10 seconds without pressing any buttons, the following output was observed (expected nothing):\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nNote: SSD output is sampled when handling an exception. The timer should be disabled, so no exceptions should be taking place.\r\n"; return(false); } //Press start, and make sure it starts counting Console.WriteLine("Pressed start; expecting SSDs to begin counting..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS)); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues = RunTestCase(mBoard, tickLimit); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS, 2)) { mMessage += "Ran your program for 20 (simulated) seconds after pressing the start button, and observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"19\" inclusive, in the correct order.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press reset, and make sure it continues counting Console.WriteLine("Pressed the lap button (at approx. 20 seconds); should have no effect on counting..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS)); mBoard.Parallel.Buttons = (uint)Buttons.Reset; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues.AddRange(RunTestCase(mBoard, tickLimit)); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 2, 4)) { mMessage += "Ran your program for 40 (simulated) seconds; pressed the reset button at 20 seconds. Observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"39\" inclusive, in the correct order. The reset button should have no effect while the timer is running.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Check lap output if (mSerialBuf.Length == 0) { mMessage += "Did not observe the lap time printed to serial port 1\r\n"; return(false); } else { double lapTime = 0.0; string rawLapTime = mSerialBuf;//.Trim(); mSerialBuf = ""; if (Regex.Match(rawLapTime, @"\r\n\d\d\.\d\d").Success) { //check numeric value double.TryParse(rawLapTime.Trim(), out lapTime); //will succeed, because of rexex match above. if (Math.Abs(20 - lapTime) > 0.200) { mMessage += "Expected to receive a lap time of approximately 20 seconds. Observed: \"" + rawLapTime.Trim() + "\"\r\n"; return(false); } } else { mMessage += "Lap output string was not in the correct format. Observed: \"" + rawLapTime.Trim() + "\". If this looks OK, make sure that you're correctly sending \\r and \\n before the numbers."; return(false); } Console.WriteLine("Lap Time Received: {0}", rawLapTime.Trim()); } //Change switches, and make sure it continues counting Console.WriteLine("Changed switch value; should have no effect..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS)); mBoard.Parallel.Switches = 0x42; ssdValues.AddRange(RunTestCase(mBoard, tickLimit)); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 3, 6)) { mMessage += "Ran your program for 60 (simulated) seconds; pressed the reset button at 20 seconds, and changed the switch value at 40 seconds. Observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"59\" inclusive, in the correct order. Changing switches should have no effect.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press stop, and make sure it stops counting Console.WriteLine("Pressed stop; expecting the value shown on the SSDs to stop counting..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS)); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues.AddRange(RunTestCase(mBoard, tickLimit)); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 3, 6)) { mMessage += "Ran your program for 80 (simulated) seconds; pressed the reset button at 20 seconds, changed the switch value at 40 seconds, and pressed 'stop' at 60 seconds. Observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"59\" inclusive, in the correct order.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press start again, and make sure it continues counting Console.WriteLine("Pressed start; expecting the value shown on the SSDs to resume counting..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS)); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues.AddRange(RunTestCase(mBoard, tickLimit)); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 4, 6)) { mMessage += "Ran your program for 100 (simulated) seconds; pressed the reset button at 20 seconds, changed the switch value at 40 seconds, pressed 'stop' at 60 seconds, and 'start' at 80 seconds. Observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"79\" inclusive, in the correct order.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Hit the lap button again Console.WriteLine("Pressed the lap button again (at approx. 80 seconds); expecting counting to continue as usual..."); mBoard.Parallel.Buttons = (uint)Buttons.Reset; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; Wag(mBoard, 1000000); //Check lap output if (mSerialBuf.Length == 0) { mMessage += "Did not observe the lap time printed to serial port 1\r\n"; return(false); } else { double lapTime = 0.0; string rawLapTime = mSerialBuf;//.Trim(); mSerialBuf = ""; if (Regex.Match(rawLapTime, @"\r\n\d\d\.\d\d").Success) { //check numeric value double.TryParse(rawLapTime.Trim(), out lapTime); //will succeed, because of rexex match above. if (Math.Abs(80 - lapTime) > 0.500) { mMessage += "Expected to receive a lap time of approximately 80 seconds. Observed: \"" + rawLapTime.Trim() + "\"\r\n"; return(false); } } else { mMessage += "Lap output string was not in the correct format. Observed: \"" + rawLapTime.Trim() + "\". If this looks OK, make sure that you're correctly sending \\r and \\n before the numbers."; return(false); } Console.WriteLine("Lap Time Received: {0}", rawLapTime.Trim()); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press stop, then reset, and make sure it goes back to zero Console.WriteLine("Pressed stop, then reset; expecting the timer to be disabled, and the value on the SSDs to be reset to zero..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS)); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.Reset; Wag(mBoard); ssdValues = RunTestCase(mBoard, tickLimit); if (!CheckSequence(ssdValues, 0, 0)) { mMessage += "After pressing the reset button (button 1) while the timer is stopped, the SSDs are expected to be set to zero and the timer is supposed to remain stopped. Observed:\r\n"; PrintSsdSequence(ssdValues); return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press start, and make sure it counts again Console.WriteLine("Pressed start again; expecting SSDs to start counting from zero..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS)); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues = RunTestCase(mBoard, tickLimit); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS, 2)) { mMessage += "Ran your program for 20 (simulated) seconds after pressing the start button, and observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"19\" inclusive, in the correct order.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } return(true); }
protected bool RunParallelTestCase(RexBoard mBoard, uint switches) { //Set switches mBoard.Parallel.Switches = switches; //Check that the SSDs don't change without waiting for a button press uint ssdOld = mBoard.Parallel.SSD & 0xFFFF; Tick(mBoard); if (ssdOld != (mBoard.Parallel.SSD & 0xFFFF)) { mMessage += "SSDs changed value without waiting for a button press.\r\n"; return(false); } //Press button 0 mBoard.Parallel.Buttons = 1; Tick(mBoard); if (mBoard.Parallel.SSD != switches) { mMessage += string.Format("SSDs did not show the correct value after pressing button 0 (observed: {0:X4}, switches: {1:X4})\r\n", mBoard.Parallel.SSD, switches); return(false); } if ((switches & 0xFFFF) % 4 == 0) { if ((mBoard.Parallel.Leds & 0xFFFF) != 0xFFFF) { mMessage += string.Format("LEDs were not high after pressing button 0 (observed: {0:X4}, switches {1:X4})\r\n", mBoard.Parallel.Leds, switches); return(false); } } else { if ((mBoard.Parallel.Leds & 0xFFFF) != 0x0000) { mMessage += string.Format("LEDs were not low after pressing button 0 (observed: {0:X4}, switches {1:X4})\r\n", mBoard.Parallel.Leds, switches); return(false); } } //Press button 1 mBoard.Parallel.Buttons = 2; Tick(mBoard); if ((mBoard.Parallel.SSD & 0xFFFF) != (~switches & 0xFFFF)) { mMessage += string.Format("SSDs did not show the correct value after pressing button 1 (observed: {0:X4}, switches: {1:X4})\r\n", mBoard.Parallel.SSD, switches); return(false); } if ((~switches & 0xFFFF) % 4 == 0) { if ((mBoard.Parallel.Leds & 0xFFFF) != 0xFFFF) { mMessage += string.Format("LEDs were not high after pressing button 1 (observed: {0:X4}, switches {1:X4})\r\n", mBoard.Parallel.Leds, switches); return(false); } } else { if ((mBoard.Parallel.Leds & 0xFFFF) != 0x0000) { mMessage += string.Format("LEDs were not low after pressing button 1 (observed: {0:X4}, switches {1:X4})\r\n", mBoard.Parallel.Leds, switches); return(false); } } //Release buttons mBoard.Parallel.Buttons = 0; Tick(mBoard); return(true); }
internal bool SupressReset = false; //set true if you don't want to reset the board when calling .Mark() /// <summary> /// Executes a test case. /// </summary> /// <param name="sendSP1">String to send to the serial port SP1</param> /// <param name="sendSP2">String to send to the serial port SP2</param> /// <param name="expectSP1">String expected to be received from SP1</param> /// <param name="expectSP2">String expected to be received from SP2</param> /// <param name="board">The board to run the tests on.</param> /// <returns>True if the test passes.</returns> new protected bool RunSerialTestCase(string sendSP1, string sendSP2, string expectSP1, string expectSP2, RexBoard board) { /*Console.WriteLine("Test Case: "); * if (sendSP1.Length > 0) * Console.WriteLine("-Sending '{0}' to SP1", sendSP1); * if (sendSP2.Length > 0) * Console.WriteLine("-Sending '{0}' to SP2", sendSP2); * if (expectSP1.Length > 0) * Console.WriteLine("-Expecting '{0}' from SP1", expectSP1); * if (expectSP2.Length > 0) * Console.WriteLine("-Expecting '{0}' from SP2", expectSP2);*/ mSP1RecvBuf = mSP2RecvBuf = ""; EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs> serialHandler1 = new EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs>(Serial1_SerialDataTransmitted); EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs> serialHandler2 = new EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs>(Serial2_SerialDataTransmitted); board.Serial1.SerialDataTransmitted += serialHandler1; board.Serial2.SerialDataTransmitted += serialHandler2; //uint pc = board.CPU.PC; //board.Reset(true); //board.CPU.PC = pc; //board.CPU.mGpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.GpRegister.sp] = 0x07b7f; //sensible value for the stack pointer //board.CPU.mGpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.GpRegister.ra] = 0x80000; //when the CPU tries to jump here, the test is over. //board.CPU.mSpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.SpRegister.evec] = 0x80000; //when the CPU tries to jump here, the test is over (should jal to exit, which is syscall). int clocks = CLOCK_CYCLE_LIMIT; //Transmit the data string toSend1 = sendSP1; string toSend2 = sendSP2; while (clocks-- > 0) { board.Tick(); if (toSend1.Length != 0 && board.Serial1.SendAsync(toSend1[0])) { toSend1 = toSend1.Substring(1); } if (toSend2.Length != 0 && board.Serial2.SendAsync(toSend2[0])) { toSend2 = toSend2.Substring(1); } } //board.CPU.PC = pc; board.Serial1.SerialDataTransmitted -= serialHandler1; board.Serial2.SerialDataTransmitted -= serialHandler2; //Make sure the Done message was received if (mSP1RecvBuf != expectSP1) { if (sendSP1.Length != 0) { mMessage += string.Format("Sent \"{0}\" to SP1\r\n", sendSP1); } if (sendSP2.Length != 0) { mMessage += string.Format("Sent \"{0}\" to SP2\r\n", sendSP2); } mMessage += string.Format("Received \"{0}\" from SP1, expected \"{1}\"\r\n", mSP1RecvBuf, expectSP1); return(false); } if (mSP2RecvBuf != expectSP2) { if (sendSP1.Length != 0) { mMessage += string.Format("Sent \"{0}\" to SP1\r\n", sendSP1); } if (sendSP2.Length != 0) { mMessage += string.Format("Sent \"{0}\" to SP2\r\n", sendSP2); } mMessage += string.Format("Received \"{0}\" from SP2, expected \"{1}\"\r\n", mSP2RecvBuf, expectSP2); return(false); } return(true); }
protected abstract bool DoSubTest(RexBoard mBoard);
internal bool SupressReset = false; //set true if you don't want to reset the board when calling .ResetBoard() /// <summary> /// Set up the state we want to restore when ResetBoard is called.false /// Note that this pair of functions won't preserve pre-initialised memory! /// </summary> protected void Initialise(RexBoard mBoard) { originalPC = mBoard.CPU.PC; }
/// <summary> /// Marks the program. /// </summary> /// <param name="mBoard">The REX board, pre-loaded with the program to test.</param> /// <returns>True if the program passes, false otherwise.</returns> public abstract bool Mark(RexBoard mBoard);
protected override bool DoSubTest(RexBoard mBoard) { //Ensure timer is set up right if (mBoard.Timer.Control != 2) { mMessage += string.Format("Timer control register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Control); return(false); } if (mBoard.Timer.Load != 2400) { mMessage += string.Format("Timer load register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Load); return(false); } //Test for 100 seconds //Sneaky speedup to 10 interrupts per second, to save time. mBoard.Timer.Count = 0; mBoard.Timer.Load = 240; List <uint> ssdValues; //10 seconds of nothing Console.WriteLine("Running program..."); int tickLimit = (int)((6.25e6 * 10) / 10); ssdValues = RunTestCase(mBoard, tickLimit); if (ssdValues.Count != 0) { mMessage += "After running the program for 10 seconds without pressing any buttons, the following output was observed (expected nothing):\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nNote: SSD output is sampled when handling an exception. The timer should be disabled, so no exceptions should be taking place.\r\n"; return(false); } //Press start, and make sure it starts counting Console.WriteLine("Pressed start; expecting SSDs to begin counting..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues = RunTestCase(mBoard, tickLimit); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS, 2)) { mMessage += "Ran your program for 20 (simulated) seconds after pressing the start button, and observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"19\" inclusive, in the correct order.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press reset, and make sure it continues counting Console.WriteLine("Pressed reset; should have no effect..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10); mBoard.Parallel.Buttons = (uint)Buttons.Reset; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues.AddRange(RunTestCase(mBoard, tickLimit)); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 2, 4)) { mMessage += "Ran your program for 40 (simulated) seconds; pressed the reset button at 20 seconds. Observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"39\" inclusive, in the correct order. The reset button should have no effect while the timer is running.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Change switches, and make sure it continues counting Console.WriteLine("Changed switch value; should have no effect..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10); mBoard.Parallel.Switches = 0x42; ssdValues.AddRange(RunTestCase(mBoard, tickLimit)); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 3, 6)) { mMessage += "Ran your program for 60 (simulated) seconds; pressed the reset button at 20 seconds, and changed the switch value at 40 seconds. Observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"59\" inclusive, in the correct order. Changing switches should have no effect.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press stop, and make sure it stops counting Console.WriteLine("Pressed stop; expecting the value shown on the SSDs to stop counting..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues.AddRange(RunTestCase(mBoard, tickLimit)); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 3, 6)) { mMessage += "Ran your program for 80 (simulated) seconds; pressed the reset button at 20 seconds, changed the switch value at 40 seconds, and pressed 'stop' at 60 seconds. Observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"59\" inclusive, in the correct order.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press start again, and make sure it continues counting Console.WriteLine("Pressed start; expecting the value shown on the SSDs to resume counting..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues.AddRange(RunTestCase(mBoard, tickLimit)); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 4, 6)) { mMessage += "Ran your program for 100 (simulated) seconds; pressed the reset button at 20 seconds, changed the switch value at 40 seconds, pressed 'stop' at 60 seconds, and 'start' at 80 seconds. Observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"79\" inclusive, in the correct order.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press stop, then reset, and make sure it goes back to zero Console.WriteLine("Pressed stop, reset; expecting the timer to be disabled, and the value on the SSDs to be reset to zero..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.Reset; Wag(mBoard); ssdValues = RunTestCase(mBoard, tickLimit); if (!CheckSequence(ssdValues, 0, 0)) { mMessage += "After pressing the reset button while the timer is stopped, the SSDs are expected to be set to zero and the timer is supposed to remain stopped. Observed:\r\n"; PrintSsdSequence(ssdValues); return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } //Press start, and make sure it counts again Console.WriteLine("Pressed start again; expecting SSDs to start counting from zero..."); tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10); mBoard.Parallel.Buttons = (uint)Buttons.Start; Wag(mBoard); mBoard.Parallel.Buttons = (uint)Buttons.None; ssdValues = RunTestCase(mBoard, tickLimit); if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS, 2)) { mMessage += "Ran your program for 20 (simulated) seconds after pressing the start button, and observed:\r\n"; PrintSsdSequence(ssdValues); mMessage += "\r\nExpected to see a sequence from \"00\" to \"19\" inclusive, in the correct order.\r\n"; return(false); } if (mBoard.Parallel.InterruptAck != 0) { mMessage += "Parallel Interrupts were not acknowledged."; return(false); } // We can't test both quitting and a functioning GPF... /* * // Press quit, and make sure it quits * Console.WriteLine("Pressed quit; expecting the program to exit..."); * mBoard.Parallel.Buttons = (uint)Buttons.Exit; * Wag(mBoard); * if (!(mBoard.CPU.PC >= WRAMPMON_ADDRESS)) * { * mMessage += "Pressed quit, and the program did not exit."; * return false; * } * * if (!(mBoard.CPU.mSpRegisters[RegisterFile.SpRegister.evec] >= WRAMPMON_ADDRESS)) * { * mMessage += "Pressed quit, but $evec doesn't point to the original exception handler.\r\n"; * return false; * } */ return(true); }
/// <summary> /// Executes a test case for questions 2 and 3. Initialise() should be called before this function. /// </summary> /// <param name="board">The RexBoard to use.</param> /// <param name="switches">The value to set the switches to for this case.</param> /// <param name="questionNumber">Either 2 or 3 depending on the question.</param> protected bool TryTestCase(RexBoard board, uint switches, bool startFirst) { ResetBoard(board); board.Parallel.Switches = switches; List <uint> ssdValues = new List <uint> { }; uint start, end; if (startFirst) { start = (switches >> 8) & 0xFF; end = switches & 0xFF; } else { end = (switches >> 8) & 0xFF; start = switches & 0xFF; } // We start the SSDs at a value different from the one they should start at when // the program begins - this value is ignored, but a change is detected unless // the student writes the same number to the same register. // Of course, this number is pretty nonsensical so it's unlikely. board.Parallel.RightSSD = 0xdead1ed; uint oldLRSSD = board.Parallel.RightSSD; for (int i = 0; i < TEST_DURATION_TICKS; i++) { // Hacky way to detect when the program is in delay() and skip the loop // Speeds up marking by around a hundred thousand times, but relies on // the delay() function allocating its specialNumber local variable to // a particular register - Perfect for wcc! if (board.CPU.mGpRegisters[RegisterFile.GpRegister.r13] == 0xdeadf00d) { // We use 5 to preserve the stack rather than jumping straight out of the // function, and change the register so this doesn't happen several times. board.CPU.PC += 5; board.CPU.mGpRegisters[RegisterFile.GpRegister.r13] = 0xbeeff00d; } board.Tick(); uint ssd = GetSSDNumber(board.Parallel.SSD); // We check the rightmost SSD since it's guaranteed to change on any counting operation if (board.Parallel.RightSSD != oldLRSSD) { // Uncomment these if you want some more verbose output during the test. // It happens so fast with the delay skip that it's not really worth keeping around. /* * try * { * Console.Write($"{new string(' ', Console.WindowWidth)}\r"); * Console.Write($"Test case {c+1}/{TEST_CASES.Length} (0x{switches:X4}): {ssd}\r"); * } * catch (System.IO.IOException) * { * // Git bash doesn't support the WindowWidth call. * } */ ssdValues.Add(ssd); // Don't exit early if there should be no counting, just to make sure they don't count. if (start != end && ssd == end) { break; } oldLRSSD = board.Parallel.RightSSD; } } try { Console.Write($"{new string(' ', Console.WindowWidth)}\r"); } catch (System.IO.IOException) { // Git bash doesn't support the WindowWidth call. } // If there should be counting, but there isn't, complain. if (ssdValues.Count == 0) { if (start == end) { return(true); } else { mMessage = $"I didn't see any counting. The switches were {switches:X4}."; return(false); } } // If there shouldn't be counting, but there is, complain. if (start == end) { if (ssdValues.Count == 1 && ssdValues[0] == start) { return(true); } else { mMessage = $"You counted when you shouldn't have! The switches were {switches:X4}."; mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues); } } // If they started or finished counting at the wrong place, complain. if (ssdValues[0] != start) { mMessage = $"Counting started at {ssdValues[0]}, not {start} when the switches were 0x{switches:X4}."; mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues); return(false); } if (ssdValues[ssdValues.Count - 1] != end) { mMessage = $"Counting ended at {ssdValues[ssdValues.Count - 1]}, not {end} when the switches were 0x{switches:X4}."; mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues); return(false); } // If they counted in a weird order, complain. // Of course it's good code when I'm duplicating everything except the direction! if (start < end) { uint previousValue = uint.MinValue; foreach (uint value in ssdValues) { if (value < previousValue) { if (value == previousValue - 1) { mMessage = $"Counting was backwards when the switches were {switches:X4}."; } else { mMessage = $"Counting did not happen in the correct order when the switches were {switches:X4}."; } mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues); return(false); } if (previousValue != uint.MinValue && value != previousValue + 1) { mMessage = $"It's not counting when you go from {previousValue} to {value}! The switches were {switches:X4}"; mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues); return(false); } } } if (start > end) { uint previousValue = uint.MaxValue; foreach (uint value in ssdValues) { if (value > previousValue) { if (value == previousValue + 1) { mMessage = $"Counting was backwards when the switches were {switches:X4}."; } else { mMessage = $"Counting did not happen in the correct order when the switches were {switches:X4}."; } mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues); return(false); } if (previousValue != uint.MaxValue && value != previousValue - 1) { mMessage = $"It's not counting when you go from {previousValue} to {value}! The switches were {switches:X4}"; mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues); return(false); } } } return(true); }