aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorm8pple <dt10@imperial.ac.uk>2014-10-16 13:44:26 +0100
committerm8pple <dt10@imperial.ac.uk>2014-10-16 13:44:26 +0100
commit39288a6d59759b1a57ba00b845a01c7973f37c09 (patch)
treefb83fd0e4acea7c9a1f7493b9251ce854e21b86e /src
parent904de2b44ee9ea9d38b71764d12fc6fe6546be3c (diff)
downloadMipsCPU-39288a6d59759b1a57ba00b845a01c7973f37c09.tar.gz
MipsCPU-39288a6d59759b1a57ba00b845a01c7973f37c09.zip
Initial push.
Diffstat (limited to 'src')
-rw-r--r--src/shared/mips_mem_ram.cpp112
-rw-r--r--src/shared/mips_test_framework.cpp234
2 files changed, 346 insertions, 0 deletions
diff --git a/src/shared/mips_mem_ram.cpp b/src/shared/mips_mem_ram.cpp
new file mode 100644
index 0000000..276388b
--- /dev/null
+++ b/src/shared/mips_mem_ram.cpp
@@ -0,0 +1,112 @@
+/* This file is an implementation of the functions
+ defined in mips_mem.h. It is designed to be
+ linked against something which needs an implementation
+ of a RAM device following that memory mapping
+ interface.
+*/
+#include "mips_mem.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+struct mips_mem_provider
+{
+ uint32_t length;
+ uint32_t blockSize;
+ uint8_t *data;
+};
+
+extern "C" mips_mem_h mips_mem_create_ram(
+ uint32_t cbMem, //!< Total number of bytes of ram
+ uint32_t blockSize //!< Granularity in bytes
+){
+ uint8_t *data=(uint8_t*)malloc(cbMem);
+ if(data==0)
+ return 0;
+
+ struct mips_mem_provider *mem=(struct mips_mem_provider*)malloc(sizeof(struct mips_mem_provider));
+ if(mem==0){
+ free(data);
+ return 0;
+ }
+
+ mem->length=cbMem;
+ mem->blockSize=blockSize;
+ mem->data=data;
+
+ return mem;
+}
+
+static mips_error mips_mem_read_write(
+ bool write,
+ mips_mem_h mem,
+ uint32_t address,
+ uint32_t length,
+ uint8_t *dataOut
+)
+{
+ if(mem==0)
+ return mips_ErrorInvalidHandle;
+
+ if(0 != (address%mem->blockSize) ){
+ return mips_ExceptionInvalidAlignment;
+ }
+ if(0 != ((address+length)%mem->blockSize)){
+ return mips_ExceptionInvalidAlignment;
+ }
+ if((address+length) > mem->length){ // A subtle bug here, maybe?
+ return mips_ExceptionInvalidAddress;
+ }
+
+ if(write){
+ for(unsigned i=0; i<length; i++){
+ mem->data[address+i]=dataOut[i];
+ }
+ }else{
+ for(unsigned i=0; i<length; i++){
+ dataOut[i]=mem->data[address+i];
+ }
+ }
+ return mips_Success;
+}
+
+mips_error mips_mem_read(
+ mips_mem_h mem, //!< Handle to target memory
+ uint32_t address, //!< Byte address to start transaction at
+ uint32_t length, //!< Number of bytes to transfer
+ uint8_t *dataOut //!< Receives the target bytes
+)
+{
+ return mips_mem_read_write(
+ false, // we want to read
+ mem,
+ address,
+ length,
+ dataOut
+ );
+}
+
+mips_error mips_mem_write(
+ mips_mem_h mem, //! Handle to target memory
+ uint32_t address, //! Byte address to start transaction at
+ uint32_t length, //! Number of bytes to transfer
+ const uint8_t *dataIn //! Receives the target bytes
+)
+{
+ return mips_mem_read_write(
+ true, // we want to write
+ mem,
+ address,
+ length,
+ (uint8_t*)dataIn
+ );
+}
+
+void mips_mem_free(mips_mem_h mem)
+{
+ if(mem){
+ free(mem->data);
+ mem->data=0;
+ free(mem);
+ }
+}
diff --git a/src/shared/mips_test_framework.cpp b/src/shared/mips_test_framework.cpp
new file mode 100644
index 0000000..b575fa1
--- /dev/null
+++ b/src/shared/mips_test_framework.cpp
@@ -0,0 +1,234 @@
+/* This file is an implementation of the functions
+ defined in mips_test.h. It is designed to be
+ linked against something which implements the
+ functions from mips_cpu.h and mips_mem.h, plus
+ some sort of main program to run the tests.
+*/
+#include "mips_test.h"
+
+#include <map>
+#include <string>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <string>
+
+static bool sg_started=false;
+
+struct test_info_t
+{
+ int testId;
+ std::string instruction;
+ int status;
+ std::string message;
+};
+
+static std::vector<test_info_t> sg_tests;
+
+struct instr_info_t
+{
+ const char *instruction;
+ const char *description;
+};
+
+static const instr_info_t sg_instructionsArray[]=
+{
+ {"<internal>", "Tests of things other than intructions."},
+ {"ADD","Add (with overflow)"},
+ {"ADDI","Add immediate (with overflow)"},
+ {"ADDIU","Add immediate unsigned (no overflow)"},
+ {"ADDU","Add unsigned (no overflow)"},
+ {"AND","Bitwise and"},
+ {"ANDI","Bitwise and immediate"},
+ {"BEQ","Branch on equal"},
+ {"BGEZ","Branch on greater than or equal to zero"},
+ {"BGEZAL","Branch on greater than or equal to zero and link"},
+ {"BGTZ","Branch on greater than zero"},
+ {"BLEZ","Branch on less than or equal to zero"},
+ {"BLTZ","Branch on less than zero"},
+ {"BLTZAL","Branch on less than zero and link"},
+ {"BNE","Branch on not equal"},
+ {"DIV","Divide"},
+ {"DIVU","Divide unsigned"},
+ {"J","Jump"},
+ {"JAL","Jump and link"},
+ {"JR","Jump register"},
+ {"LB","Load byte"},
+ {"LBU","Load byte unsigned"},
+ {"LUI","Load upper immediate"},
+ {"LW","Load word"},
+ {"LWL","Load word left"},
+ {"LWR","Load word right"},
+ {"MFHI","Move from HI"},
+ {"MFLO","Move from LO"},
+ {"MULT","Multiply"},
+ {"MULTU","Multiply unsigned"},
+ {"OR","Bitwise or"},
+ {"ORI","Bitwise or immediate"},
+ {"SB","Store byte"},
+ {"SH","Store half-word"},
+ {"SLL","Shift left logical"},
+ {"SLLV","Shift left logical variable"},
+ {"SLT","Set on less than (signed)"},
+ {"SLTI","Set on less than immediate (signed)"},
+ {"SLTIU","Set on less than immediate unsigned"},
+ {"SLTU","Set on less than unsigned"},
+ {"SRA","Shift right arithmetic"},
+ {"SRL","Shift right logical"},
+ {"SRLV","Shift right logical variable"},
+ {"SUB","Subtract"},
+ {"SUBU","Subtract unsigned"},
+ {"SW","Store word"},
+ {"XOR","Bitwise exclusive or"},
+ {"XORI","Bitwise exclusive or immediate"}
+};
+static const unsigned sg_instructionsCount = sizeof(sg_instructionsArray)/sizeof(sg_instructionsArray[0]);
+
+static std::set<std::string> sg_knownInstructions;
+
+
+extern "C" void mips_test_begin_suite()
+{
+ if(sg_started){
+ fprintf(stderr, "Error:mips_test_begin_suite - Test suite has already been started\n");
+ exit(1);
+ }
+
+ // Build up a list of known instruction names
+ for(unsigned i=0; i<sg_instructionsCount; i++){
+ sg_knownInstructions.insert(std::string(sg_instructionsArray[i].instruction));
+ }
+
+ sg_started=true;
+}
+
+extern "C" int mips_test_begin_test(const char *instruction)
+{
+ if(!sg_started){
+ fprintf(stderr, "Error:mips_test_begin_test - Test suite has not been started with mips_test_begin_suite.\n");
+ exit(1);
+ }
+
+ if(sg_tests.size()>0){
+ if(sg_tests.back().status == -1){
+ fprintf(stderr, "Error:mips_test_begin_test - Attempt to start new test of '%s', but previous test with id %u has not been completed.\n", instruction, sg_tests.back().testId);
+ exit(1);
+ }
+ }
+
+ int testId=sg_tests.size();
+
+ test_info_t info;
+ info.testId=testId;
+
+ info.instruction=instruction; // We want the string in upper case (shouting!)
+ std::transform(info.instruction.begin(), info.instruction.end(), info.instruction.begin(), ::toupper);
+
+ if(sg_knownInstructions.find(info.instruction)==sg_knownInstructions.end()){
+ fprintf(stderr, "Warning:mips_test_begin_test - Unknown instruction '%s', might want to check the spelling.\n", instruction);
+ }
+
+ info.status=-1;
+ sg_tests.push_back(info);
+
+ return testId;
+}
+
+extern "C" void mips_test_end_test(int testId, int passed, const char *msg)
+{
+ if(!sg_started){
+ fprintf(stderr, "Error:mips_test_finish_test - Test suite has not been started with mips_test_begin_suite.");
+ exit(1);
+ }
+
+ if(sg_tests.size()==0){
+ fprintf(stderr, "Error:mips_test_finish_test - No tests have been started.\n");
+ exit(1);
+ }
+ if(sg_tests.back().testId!=testId){
+ fprintf(stderr, "Error:mips_test_finish_test - Attempt to finish test %u, but last test started was %u.\n", testId, sg_tests.back().testId);
+ exit(1);
+ }
+ if(sg_tests.back().status!=-1){
+ fprintf(stderr, "Error:mips_test_finish_test - Attempt to finish test %u, but it already finished with status %u.\n", testId, sg_tests.back().status);
+ exit(1);
+ }
+
+ sg_tests.back().status=passed ? 1 : 0;
+ if(msg){
+ sg_tests.back().message=msg;
+ }
+}
+
+
+extern "C" void mips_test_end_suite()
+{
+ if(!sg_started){
+ fprintf(stderr, "Error:mips_test_finish_suite - Test suite has not been started with mips_test_begin_suite.\n");
+ exit(1);
+ }
+ if(sg_tests.size()==0){
+ fprintf(stderr, "Error:mips_test_finish_suite - No tests have been executed.\n");
+ exit(1);
+ }
+ if(sg_tests.back().status==-1){
+ fprintf(stderr, "Error:mips_test_finish_suite - The final test has not been completed yet.\n");
+ exit(1);
+ }
+
+ // Now we will go through an collect some statistics about what happened
+
+ // Build a map from instruction name to a pair of (tests,passed)
+ typedef std::map<std::string, std::pair<int,int> > stats_t;
+ stats_t statistics;
+
+ for(unsigned i=0; i<sg_tests.size(); i++){
+ test_info_t info=sg_tests[i];
+
+ statistics[info.instruction].first++; // count all tests
+ if(info.status==1){
+ statistics[info.instruction].second++; // count the ones that passed
+ }
+ }
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "| Instruction | tests | passed | success |\n");
+ fprintf(stderr, "+-------------+--------+--------+---------+\n");
+
+ // Work out what happened for each instruction
+ int totalTested=0;
+ int totalNotWorking=0;
+ int totalPartiallyWorking=0;
+ int totalFullyWorking=0;
+
+ stats_t::const_iterator it=statistics.begin();
+ while(it!=statistics.end()){
+ std::string name=it->first;
+ int total=it->second.first;
+ int passed=it->second.second;
+
+ totalTested++;
+ if(passed==0){
+ totalNotWorking++;
+ }else if(passed<total){
+ totalPartiallyWorking++;
+ }else{
+ totalFullyWorking++;
+ }
+
+ fprintf(stderr, "|%12s | %4u | %4u | %5.1lf%% |\n", name.c_str(), total, passed, 100.0*passed/(double)total);
+
+ if(sg_knownInstructions.find(name)==sg_knownInstructions.end()){
+ fprintf(stderr, "+ Warning: previous instruction not known +\n");
+ }
+
+ ++it;
+ }
+
+ fprintf(stderr, "+-------------+--------+--------+---------+\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Total instructions tested: %3u\n", totalTested);
+ fprintf(stderr, "Fully working : %3u (%5.1lf%%)\n", totalFullyWorking, 100.0*totalFullyWorking/(double)totalTested);
+ fprintf(stderr, "Partially working : %3u (%5.1lf%%)\n", totalPartiallyWorking, 100.0*totalPartiallyWorking/(double)totalTested);
+ fprintf(stderr, "Not working at all : %3u (%5.1lf%%)\n", totalNotWorking, 100.0*totalNotWorking/(double)totalTested);
+}