arch2-2016-cw's People
Forkers
jeg114 tomhartley fyquah ppp2211 roastedpork jiantastic malharjajoo diederikvink luisi-at mk2020 royalnine yhc615 divyanshmanocha liaskast morristech saginskyarch2-2016-cw's Issues
"Shift" section for Arithmetic R Instructions
According to docs, SUBU (amongst others like ADD, SUB that isn't shift) has a shift
value of 0. if shift != 0
should it be an invalid instruction?
Endian
Hi, I'm still confused by load and store and whether the endianness of a byte/halfword/word would need to be changed when putting it in or taking it out of memory. Was wondering if could talk it through in computing labs at 1 today?
No Output in Email from Today's Pre-Deadline Test
There is no raw .txt log/output file attached in the automated email
The link in the readme is to a guide for MIPS IV, however the readme states we're using a subset of MIPS I
I am wondering if this is a mistake, or if I'm misunderstanding, or if MIPS IV is similar enough to MIPS I that the document (http://math-atlas.sourceforge.net/devel/assembly/mips-iv.pdf) linked is still relevant.
Previous Instruction Not Known
I've automated my test harness but now every time I pass mips_test_begin_test
an operation mnemonic string, ADD
for instance, the output displays that test has run successfully and then adds the warning that the previous instruction is not known:
The operation is quite clearly legal, so what's the problem? Am I right in thinking that the internal lookup uses an exact match for the string that goes in mips_test_begin_test
?
Testing mips_error return values
This question relates to issue #16 however I am still unsure of the return value from instructions which result in undefined behaviour. The specification states that operations such as divide by 0 result in an undefined arithmetic result and the DIV operation does not return any arithmetic exception. Similarly there are scenarios where a MFHI or MFLO operation could access registers with undefined values.
The specification also states when an Address Error should be thrown, however mips_core defines both mips_ExceptionInvalidAlignment and mips_ExceptionInvalidAddress.
My questions are:
- Are these scenarios examples where ExceptionBreak and AccessViolation should be thrown or should both return mips_Success? (if not, could you provide an example of where these errors could be used?)
- If an incorrect error/exception is thrown should we report test failed? (more specifically: if an address is invalid for more than one reason, should it matter which of the 2 address errors are thrown?)
USER_CPU_SRC nested directories
I figure it is appropriate to permit source files (not just header files) in nested directories at src/<USER-NAME>
. The Makefile currently doesn't seem to compile source files in nested directories.
One obvious (I think) reason to support such structure is to allow test files and source files to be in different directories. Eg, I think it is much more appropriate to have src/eie2ugs/main
and src/eie2ugs/test/
instead of having everything in src/eie2ugs
.
Edit: I made a commit that implements the above at my fork (fyquah@2fc9ad5)
Function pointers
Updated: for context, someone was asking about function pointers
and the syntax, so this is what I typed out while talking about it. You
do not need to use function pointers, they are just one of many ways
of structuring code. If/else or switch are also fine.
It is a fragment only, and won't compile in the current form.
Function pointers are mainly used in low-level code where C must
be used. Part of the reason the MIPS API looks the way it does is
because I'm deliberately avoiding things that weren't covered in
1st year programming, but in C++ also can use:
- Polymorphism : virtual functions which you'll see in the OOP course
- std::function / lambda functions : this is closer to the concept of a C function
pointer, but is much richer as it can contain both code and state.
uint32_t op_addu(uint32_t va, uint32_t vb)
{
return va+vb;
}
uint32_t op_subu(uint32_t va, uint32_t vb)
{
return va-vb;
}
typedef uint32_t (*op_t)(uint32_t, uint32_t);
struct op_entry{
const char *name;
unsigned function_code;
op_t op;
};
op_entry ops[]={
{ "addu", 3746, op_addu }
};
uint32_t va=...
for(unsigend i=0;i<nOps;i++){
if(ops[i].function_code==function_c ode){
vr=ops[i].op(va,vb);
}
}
Subtle bug in mem code
Will the subtle bug still be present in the code used for testing?
When passing a negative address (or an extremely large positive address, but that is extremely unlikely) to mem_read, address+length
may overflow, causing the condition (address+length) > mem->length
to return false (and ultimately a segmentation fault (core dumped) on my program). This can be fixed by adding another condition: address > (UINT32_MAX - length)
. It can also be prevented on the CPU side by building in a safeguard that prevents passing negative values to the memory handler.
I can build a safeguard into my CPU, but others' CPUs may not have the same protection and will crash my test program when I test for mips_ExceptionInvalidAddress from a branch to a negative address. Shall I not test that condition or shall I assume the test code will not have the subtle bug?
Undefined behaviour of MFHI, MFLO, MTHI, MTLO
MFHI
and MFLO
give undefined behavior when the following two instructions contain an instruction that modifies HI
or LO
and MTHI
and MTLO
give undefined results when a previously modified HI
or LO
has not yet been read by a MFHI
and MFLO
, if I understood correctly the specifications.
However I assume that this undefined behavior is caused by the hardware implementation of these instructions. Since our simulator is free from these restrictions it can implement these instructions without undefined behavior. Should we make them have undefined behavior, by setting the output to be a random number for example?
Making them have undefined behavior would mean having a more precise simulator because programs that would crash on the MIPS due to this undefined behavior would also do under the simulator. However the correct output can also be considered to be undefined behavior.
Naming subfolders
Do we need to follow the name requirements for names of subfolders as well? In the current version of the makefile all subfolders will get compiled. However, that can cause some issues if tests are swapped around between different submissions. E.g. right now my test suite needs SOME of the subfolders to run, but not ALL of them. There is no clear way to distinguish the subfolders you need only from their names, though, without reading the code and tracking all headers back. Possible solution might be to name all test related files/subfolders test_mips_* and all cpu-related ones mips_cpu_*.
Potential ambiguity with regard to manually setting PC just after a jump.
As discussed - if, for example, right after a J(ump) instruction at X to Z, if PC is set to Y, I can invisage a couple of outcomes. The spec clearly states that using mips_cpu_set_pc sets the address of the next instruction. So:
Next instruction after J is Y, and them Y+4, etc.
or -
Next instruction after J is Y, and then Z, Z+4, etc.
The first option might be more useful from a debugging/testing perspective, given that's what mips_cpu_set_pc is designed for.
Final test
I got a completely different output when I clone the git repository, put my source files in, and then make and run the program to the output shown from the self tests today. Are you sure you used the correct blackboard submission? (I've submitted several times?)
Failing that, are you using the latest version on Git?
I ask because it was almost as if it were someone else's code or CPU, there were printed bits of code where there are no corresponding fprintf or cerr statements in my code, and the result was completely different.
Printing to stderr while running test_mips.cpp
Is it okay to print messages to stderr?
I print ongoing stats so it's easy for the user to pinpoint the error that caused the problem i.e.
Testing testid
Description: msg
Result: pass/fail
And also warnings before testing dangerous things that may crash the CPU, like divide by 0 or invalid handle test.
In-person questions
A few people have asked about meeting times in person, and I said
"post an issue", but there seems to be some reluctance, or possibly
the github interface is too poor.
I've been tied up with graduation today, but tomorrow (Thu 20th)
I can do lunch-time (13:00-14:00). I'll go to the level 3 lab, and
hang around.
LWL
Hi, I was just wondering if there was a clearer description of LWL than that given inmath-atlas.sourceforge.net/devel/assembly/mips-iv.pdf
as I don't understand what they mean by the least significant part of the word in the register is unchanged? Cheers
Test success percentage not displaying correctly
In mips_test_framework.cpp, the percentage display for the tests does not work for me as it always displays -0.0% for everything (I am using Windows 7 with the mingw32 g++ compiler and linker). I think it is related to the %5.1lf specifier. I changed this to %.5.1f and it seems to work.
mips_cpu_get_register
My questions are regarding the following function:
mips_error mips_cpu_get_register(mips_cpu_h state, unsigned index, uint32_t *value);
It is my understanding that this function checks a register from the mips_cpu_h to
see whether it contains a certain value. If this value is correct then the function
will return mips_Success, and everything is ok. However, I assume we must code
for other unexpected results. For example, the index can only take on a value
between 0 and 31, so if the index is not in this range, we have an error. Which error
does this come under? You could argue it would fall into either of these categories
in my opinion:mips_ExceptionInvalidAddress=0x2002; mips_ExceptionAccessViolation=0x2004; mips_ErrorInvalidArgument=0x1001;
Also what is the significance of passing the value as a pointer, rather than just a uint32_t value?
Does this also mean that the process the test_mips.cpp file uses is up to us? For example, we might > include tests for an index that is out of range as well as a test for one that is in range.
These may seem like simple questions but I just wanted to be sure I understand the basics
before getting too deep into the code.
Exception behaviour
Do we increment the program counter or not?
If an exception or error occurs, the CPU and memory state
should be left unchanged. This is so that the user can
inspect what happened and find out what went wrong.
From this I assume not.
2's Complement Modulo
When calculating the remainder of the DIV
instruction the specs say it should be
r ← GPR[rs]31..0 mod GPR[rt]31..0
where mod
is defined earlier in the specs as 2’s complement modulo.
From this I understand that it should have a sign though there are several ways of deciding the sign, should it be according to this formula (a/b)*b + b*(a mod b) = a
?
Do we submit (formative) coursework to the Arch2-CW link?
The description of the link says it is for the deadline of 28th. Do we submit to this for the formative assessment and then re-submit to the same place next week?
Makefile is not fully commented
Only the first 5 lines are commented, and I don't understand what the rest means. Please could you comment the rest of the code blocks explaining what they do as you have the first 5 lines? Thank you.
Disassembling instructions
Hi, we were wondering if it is necessary to create a function to disassemble an instruction such as "addu r1, r2, r3", turning this into binary code. If so, where should it go in the program? We're unclear as to whether writing such a function is just to make our lives easier later or if it is a requirement within the cpu?
Error in sg_instructionsArray[]
In the array of instructions, the following can be found:
{"LH","Load word"},
{"LHU","Load word unsigned"},
This causes some confusion as load word already has the mnemonic of LW/LWU.
This should be corrected to "load half word", based on one of the other instructions given.
Potential issue with identically named functions in both the test framework & simulator.
[Just discovered by cross-checking code & sim with someone else] -
If two functions happen to be named exactly the same in the test & simulator files, the linker will fail with a 'duplicate symbol' warning. How should we avoid this?
Potential solution: Define 2 namespace prefixes for test & simulator to prefix all functions with - however that could be a pain for everyone to do. Another solution is just to hope it doesn't happen, but with simple function names for example 'init' this is optimistic at best.
MFHI
When trying to implement MFHI, is it possible to define a new 'reghi' register when creating a cpu and if so does this need a special name? Otherwise in the tester side of it I would like to set a specialised reghi register to be a value, then try and move it to a new register using my function and read from this new register but without a standardised name I don't see how this will work with other people's cpus?
Whether to model the different components of the CPU as functions
I was wondering if in the simulator we should define an ALU function,
through which all instructions that would use the ALU in real life would
make use of. This could also be done with other components of the CPU.Or, would it be more efficient to simply add/subtract etc within each
instruction’s function directly?
Overwriting registers
When creating functions that require a destination register for the result, how do you choose which to use to avoid overwriting values already in them?
Blackboard not allowing multiple submissions
Blackboard doesn't seem to let anyone start a new submission after having submitted something before, despite the description stating "unlimited uploads available". Not sure if it's a glitch with Blackboard, or human error.
How do we run fragments/ programs?
I was trying to run fragments/run_fibonacci as in the comments in the makefile.
After running:make fragments/run_fibonacci
, the output of ls fragments
contains:
f_addu.c f_addu-mips.diss f_fibonacci-mips.bin run_addu.cpp
f_addu-mips.bin f_fibonacci.c f_fibonacci-mips.diss run_fibonacci.cpp
There is no run_fibonacci binary/.exe/.o
I added $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LFLAGS) $(LDLIBS)
below fragments/run_fibonacci : $(DEFAULT_OBJECTS) $(USER_CPU_OBJECTS)
But then got this error:
g++ -W -Wall -g -I include -std=c++11 -o fragments/run_fibonacci src/shared/mips_test_framework.o src/shared/mips_mem_ram.o src/gsp14/mips_cpu.o src/gsp14/mips_cpu_extra.o
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o: In function '_start':
(.text+0x20): undefined reference to 'main'
collect2: error: ld returned 1 exit status
makefile:81: recipe for target 'fragments/run_fibonacci' failed
make: *** [fragments/run_fibonacci] Error 1
Txt files, how to include them?
Is there a way how can we use txt files for storing and loading? The file can't be opened without plugging in the entire file path. Thank you!
increase memory limit
Would it be possible for the allocation limit in the memory implementation in mips_mem_ram.cpp
to be increased to 512 MB?
makefile points to wrong sources
The spec defines three options for the paths of source files that get compiled into the program, of which one is src/[your_login]/mips_cpu_*/*.cpp
However, the makefile contains the line $(wildcard src/$(LOGIN)/mips_cpu/*.cpp)
The two do not match (the makefile is missing a wildcard in the directory name).
The same goes for the test suites (test_mips_*/*
).
Also, I noticed that the mips_cpu.c skeleton from 2014 is indeed a C file, which wouldn't get compiled according to the spec (which only allows cpp). It's a minor thing, but maybe the makefile could be set to include C files as well as C++?
Invalid Alignment vs Address Error Exception
When reading from memory if the address is not correctly aligned mips_mem_ram
returns an mips_ExceptionInvalidAlignment
however according to several the
specifications instructions such as JR
:
"The effective target address in GPR rs must be naturally aligned. If either of the two least-significant bits are not -zero, then an Address Error exception occurs, not for the jump instruction, but when the branch target is subsequently fetched as an instruction."
I can´t find any mention to mips_ExceptionInvalidAlignment
so my issue is should we return an mips_ExceptionInvalidAlignment
when we are expected to return an Address Error exception, or should mips_ExceptionInvalidAlignment
be changed to mips_ExceptionAddressError
Ambiguity in the instruction complexities
In the list of instructions to implement, the move instructions are listed with complexity "3 XXXX". This is also the case for DIVU and MULTU. Also, SLLV is listed as "3 XX". Are these instructions 3 or 4 (or 2)?
PC after exception
Sorry to bring this up again. It directly relates to issue #30, however I would like some more clarification.
- If the PC was not to be incremented should an exception occur, does that mean the instructions following the failed instruction will not run to preserve the current state?
- Therefore in order to run the instructions that follow, the test file needs to force increment the PC if and when an exception occurs?
Test dependencies
Is it okay if a few of my tests depend on other things working?
For the MULT/DIV example it's obvious that there is some kind of dependency on MFHI/MFLO for testing.
However in testing my branching/jumping instructions, I rely on a simple ADDU in the delay slot and at the target location to test whether the delay slot and jumping both work correctly... However, if ADDU doesn't work, my testing program may (wrongly) say none of the JUMP/BRANCH functions work.
But, I have to use some kind of instruction other than J to test whether the CPU correctly executes the delay slot instruction and also correctly executes the instruction jumped to. ADDU seems the most obvious choice...
Soft deadline
Would it be possible to get an update through here when you have pulled all of the files from blackboard for the soft deadline David?
This way we can keep updating our files if we keep working throughout the evening.
In-person questions v2
No-one asked, but I figure there might be latent demand...
I'll be available 13:00 in level 3 labs tomorrow (Fri 21st).
Is return address set unconditionally or conditionally for bltzal and bgezal?
The documentation seems a little bit unclear about whether or not bltzal
and bgezal
set the return address even if the branch isn't taken. The wording seems to imply that it does, which doesn't make much sense to me (if this is the case... why is that useful? Or is it a hardware limitation?).
On the same note, I have tested in Spim which sets R31 unconditionally and in Mars which sets it conditionally. Is this another case of simulators being inconsistent/wrong?
Possible ambiguity with execution of load byte instruction
The load byte instruction would not need a memory address that is a multiple of 4 as accessing bytes within the word should be possible. However as the block size has been specified as 4 for the simulated processor, and also memory access to any unaligned address throws an InvalidAllignment error, accessing a single byte within the word is not possible in our cpu.
Possible workarounds:
- Only allow aligned memory reads, thus allowing access to only the least significant byte of a memory word.
- Within our cpu code, identify the word within which the required byte is in and pass an aligned address for the memory read. On receiving the word, isolate the required byte.
Option 1 seems to be the more valid option as the restriction of a minimum data transfer of 4 bytes would by default place a restriction on single byte operations and also option 2 does not seem as a calculation that would happen within the cpu.
Are registers big endian?
For the load word and store word operation, do we assume the registers are big endian (and the memory is little endian, therefore we have to convert to and from big endian for load and store)?
Branch Exceptions
All questions are related to memory addresses, so I thought only one issue would be most appropriate. I could not find these mentioned in the specifications
- Should I indicate somehow when PC overflows and returns to 0
- When I execute a branch with negative immediate argument and the resulting number for the address is negative, do I generate an exception? What if the immediate is positive and the resulting address makes PC overflow? If yes, what is the expected exception: mips_ExceptionInvalidAddress or mips_ExceptionAccessViolation?
3.If in fact exception is generated: say my branch instruction is at address 0x04. Do I signal the exception after the branch instruction itself, or after I execute the instruction at address 0x08
Change in private header does not cause makefile to recompile
If one of the private header files change, but the C/C++ file that depends on it doesn't change, the makefile assumes there has been no change and so doesn't recompile the C/C++
A solution would probably involve makefile dependencies, but I'm not well versed enough to work it out.
submission dates incorrect
In the main readme, under https://github.com/m8pple/arch2-2016-cw#assessment-criteria the dates for the formative ungraded assessment read 21st of October, both for submission and feedback. Is it meant to be Monday 24th?
Handling invalid instructions
What should happen, when a half existing instruction is given to the CPU? Such as an ADD instruction, with non-zero shift bits. Is it OK that the CPU returns mips_ErrorNotImplemented=0x1000,
or should it return mips_ExceptionInvalidInstruction=0x2004,
Should we implement JALR?
JALR is listed as required instruction in the Readme but does not exist as entry in static const instr_info_t sg_instructionsArray[]
in mips_test_framework.cpp. Do we need to implement it?
Delay slot queries
According to the MIPS specification, the instruction after a jump instruction (the delay slot) is executed before the jump is taken. I can appreciate why this is from a hardware point of view (meaning that the pipelined instruction isn't just thrown away).
However, when emulating programs in Spim, I've noticed that it doesn't seem to execute the instruction in the delay slot. Is this a result of the assembler simply filling the delay slot with a nop
? Should we be implementing delay slot execution or just jumping directly?
This possibly also highlights places where simulators differ from the specification...
Should we made unused bits don't cares or return an error if they are nonzero?
How do we test mips_cpu_free?
Anything I do to the cpu pointer after I've 'freed' it crashes the program.
I've used mips_cpu_impl *state = new mips_cpu_impl;
to create the cpu and delete state
to free it.
I could use 'smart' pointers or malloc
and free
, but can't guarantee others will have done that, so testing their mips_cpu_free's will crash my program.
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.