Input/Output clarification.
MarcoSelvatici opened this issue · comments
Hi,
I was reading issue #14 about to better understand how I/O is supposed to work when using read/store operations diffrent from sw and lw.
I found this sentence:
"From the hardware IO devices point of view (e.g. a memory mapped UART)
SWL, SWR, SW, SH, and SL all look exactly the same. They are given an
aligned address that is being written, and 32-bit value to write. In order to
distinguish which bytes should be written, then a byte enable signals
are used. This was discussed in one of the lectures in the context of SB;
it was the 4-bit mask which is used to decide which of the bytes in memory
is actually updated."
If I understand correctly, this means that:
- a SW at ADDR_PUTC will write bytes [ADDR_PUTC, ADDR_PUTC + 3] and the output will be (like in all the the cases) the content of ADDR_PUTC + 3.
- a SB operation that writes any of the bytes [ADDR_PUTC, ADDR_PUTC + 3] will trigger an output operation, and the displayed byte will be the one at ADDR_PUTC + 3 (even if the SB operation does not change it, e.g. a SB at ADDR_PUTC + 2).
- for SH, the reasoning is similar to SB.
Similar behavior would also be for LB and LH at ADDR_GETC. I.e. any valid load operation in the range [ADDR_GETC, ADDR_GETC+ 3] will trigger a reading from stdin.
Is my understanding correct?
Thanks
This sounds like a perfectly fine interpretation, and seems to meet the requirements I've just clarified in #40 (apart from how to handle EOF and 0xFF distinction for LB/SB).
Just in case this isn't completely clear, do bear in mind that there is no memory at addresses 0x30000000..7, so "the content of ADDR_PUTC + 3" (strictly) doesn't exist. A more accurate way to interpret it would be "the content of [memory at address] ADDR_PUTC + 3 [if such memory existed]."
Would this mean that even if the simulator had previously outputted a value, such as 0xFF, nothing should be stored at address 0x30000007 and if I/O is triggered by an SB to address 0x30000004..6 the output should be 0? Or should it take the previous output that would have been stored if the memory location did exist?
Nothing should be stored at 0x30000007 full stop -- there's nowhere to store it. The suggestion to output 0 when hitting 0x30000004..6 with a SB sounds sensible to me; knowledge of "previous output" would require memory which, as we've just established, doesn't exist!
To consolidate all the wonderful discussions on this topic scattered throughout the spec, I would like to propose a collated interpretation for Simulator I/O, so as to clear up the ambiguities for everyone and have fewer people have to read 5 different issue threads to understand what is going on.
Since we are modelling a UART hardware with addr_getc and addr_putc, let us assume two 32-bit wide buses from our CPU to I/O hardware, and an 8-bit UART.
Given a UART that receives and returns data through an 8-bit parallel data bus, what we have now is our 32-bit MIPS data bus (getC and putC), with 24 bits connected to a mux between logic 0 (ground) and logic 1 (high), and 8 bits connected to their respective UART terminals. The MUX is present for sign extension in the event of an EOF and a correctly-implemented control signal for the MUX is assumed to be in place.
As we have previously defined, these data buses contain no memory elements, and can only be read/written at the time of I/O. Given our big-endian environment, it makes the most sense to connect this 8-bit bus to 0x30000003 for Rx and 0x30000007 for Tx (the respective LSBytes of their 4-byte spaces).
Given this hardware configuration, let us assume that a single instruction call to a valid area of memory in this I/O region will trigger / mark the receipt of a UART Tx start bit, thus allowing the read/write of a single 8-bit char to/from the UART. However, given that the UART is only connected to 0x30000003 and 0x30000007, only reads from these (connected) regions of the data bus would return values. All other reads would return 0. EOF would be an exceptional case to this, where the other reads would return 0xFF.
All this is a bit verbose, so I'll illustrate with a few scenarios and a summary. Other scenarios such as Load byte or store byte should be simple to infer from these.
Load Word at 0x30000000: (all others within getc would be unaligned and return an exception)
- I/O is triggered once
- the whole word is read, but only the byte read at 0x30000003 returns the fetched character. all other reads return 0 (unless EOF)
Load Halfword at a. 0x30000000 and b. 0x30000002
- I/O is triggered once
- a half word is read and for case a., the read returns 0, unless eof. for b,. only the byte read at 0x30000003 returns the character.
Similarly for writing data with putc, only writes to 0x30000007 would output data.
TL;DR SUMMARY
- I/O is triggered as long as a load/store instruction correctly aligns itself to the I/O space, according to its granularity.
- Only reads at 0x30000003 return non-zero input values, and writes at 0x30000007, due to the nature of the hardware connections. Other reads and writes should read/write 0, except in the case of an EOF.
To the best of my ability, this interpretation above conforms to the points discussed in all previous discussions on I/O, particularly #14. I may be completely out of my depth here, and will humbly accept if I have gotten this completely wrong. However, I thought this to be a sensible definition given all the information so far, and my current knowledge of the topic. If we all accept this, I hope it saves other people time and clears up ambiguity in the spec.
Regards
Darrick
This sounds like a good interpretation to me. There is potential confusion with "4 reads" and "2 reads" (presumably these are supposed to be "reads x bytes in length" where x is 4 or 2), but otherwise it is clear. As far as the spec is concerned, the only requirements are:
- Loads/stores within [ADDR_GETC,ADDR_GETC+3]/[ADDR_PUTC,ADDR_PUTC+3] trigger I/O (as defined by the memory map).
- When dealing with multiple bytes, they are always treated in big-endian order.
- EOF is -1, whichever data length is accessed. For words it is therefore 0xFFFFFFFF, half-words 0xFFFF and bytes 0xFF.
I particularly like the additional restriction that the least-significant bytes (ADDR_GETC+3, ADDR_PUTC+3) are the ones where characters always appear/are taken from, since this reflects sensible use of hardware. Still, while this may be incorporated into the spec for future years, I won't do this this year, so other interpretations remain possible as long as they obey the rules listed above.
What should lb at 0x30000000 return if the current input char is EOF? Should it be -1 or 0?
If you choose to obey the tightened specification above, -1.
"If you choose to obey..": does this mean there is no right answer and one shouldn't test for it?
Certain things are defined by the coursework spec and/or the ISA. See above for those that are required per the spec.
Just to clarify: lbu at 0x30000000 should also return -1 when the input char is EOF, correct? It doesn't matter that it is an unsigned load
That depends on your definition of "return." If you mean "from I/O," then yes, I think so. If you mean "to the target register," I don't.
If you choose to obey the tightened specification above, -1.
Does this mean we have to send -1 to the register when read from address 0x30000000 to 0x30000002? And do we consume the char from stdin?
Would this kind of read be tested, or should be treat as undefined? I think it would make sense to write 0 to register (or -1 only when stdin is empty), and only write -1 to register when there is an actual EOF (empty stdin).
If it's not in the spec or defined by the ISA, it's implementation defined. I can't test implementation-defined behaviour since there's no single, correct behaviour. The parts of this topic that are not implementation-defined are covered thoroughly in this thread's earlier posts.