I'm trying to learn about MIPS pipe-lining and the hazards associated to them. I'm having trouble picturing what a structural hazard looks like in MIPS instructions.
I've read that it is a situation where two (or more) instructions
require the use of a given hardware resource at the same time. And I've seen examples shown in clock cycles before. But can anyone just provide a simple MIPS instruction set example for me to see? I'm having difficulty finding one online. Just see lots of examples for data hazards and that's not what I'm looking for. Thanks!
It is hard for you to come by this problem because it's usually resolved in the HW architecture...
Here are two examples:
Assume a write is made to the register file (RF) during stage 5 (WB) and a read is made to the same register on the RF on stage 2 (ID) at the same time. This is a structural hazard because two instructions are trying to access the same resource at the same clock cycle (what value will be read?). This can be resolved (in the HW), for instance, by splitting the RF access to two clock phases, write on HIGH and read on LOW. Moreover, if you think about it, a structural hazard is why there are separate 2 read ports and 1 write port in the RF.
Assume an instruction is being fetch from memory (stage 1, IF) and another read/write is done to the memory on stage 4 (MEM). Again, same resource accessed on the same cycle. This was resolved by separating the data and instruction memories (harvard architecture). It may look obvious to you but you can lookup Princeton Architecture and see an example to a unified memory.
So if we take the first example for instance: any set of instructions with a load (lw) command to the same register as in a R-type command (like add) that follows after two other instructions will do the trick:
lw $8, 100($9)
add $10, $11, $12
add $10, $11, $12
add $10, $8, $12
Hope that helps.
This might work, but I'm not a big MIPS person:
add $t0, $t1, $t2
sw $t3, 0($t4)
sub $t5, $t6, $t7
sub $t8, $t9, $t0
sw $t0, 0($s0)
Related
How does MIPS's assembler labels and J type instruction work?
I am currently making a MIPS simulator using C++ and came into a big question. How exactly does MIPS assembler manage label's and their address while on a J type instruction?
Let's assume that we have a following code. Also let's assume that start: starts at 0x00400000. Comments after code represent where the machine codes will be stored in memory.
start:
andi $t0, $t0, 0 # 0x0040 0000
andi $t1, $t1, 0 # 0x0040 0004
andi $t2, $t2, 0 # 0x0040 0008
addi $t3, $t3, 4 # 0x0040 000C
loop:
addi $t2, $t2, 1 # 0x0040 0010
beq $t2, $t3, exit # 0x0040 0014
j loop # 0x0040 0018
exit:
addi $t0, $t0, 1000 # 0x0040 002C
As I am understanding right at the moment, j loop expression will set PC as 0x0040 0010.
When J type instruction uses 32 bits and with MSB 6 bits as its opcode, it only has 26 bits left to represent address of instruction. Then how is it possible to represent 32 bit address system using only 26 bits?
With the example above, it can represent 0x00400010 with only 24bits. However, in references, text segment is located from 0x00400000 to 0x10000000 which needs 32bit to represent.
I have tried to understand this using MARS simulator, however it just represents j loop as j 0x00400010 which seems nonsense to me since 0x00400010 is 32 bits.
My current guess
One of my current guesses is following.
Assembler saves the loop: label's address into some memory address that is reachable by 26 bits. Then when expression j loop is called, label loop is translated to the memory address that contains 0x00400010 For example, 0x00400010 is saved in some address like 0x00300000 and when j loop is called, loop is translated into 0x00300000 and it is able to get value from 0x00300000 and reach out 0x00400010. (This is just one of my guess)
You have a number of questions here.
First, let's try to differentiate between the assembler's operation and the MIPS machine code that it generates and the processor executes.
The assembler manages labels and address in two ways. First, it has a symbol table, which is like a dictionary, a data structure of key-value pairs where the names are keys and the addresses (that those names will refer to when the program is running) are the values in the pairs.
Second, the assembler manages the code and data sections with a location counter. That location counter advances each time the program provides some code or data. When new label is defined, the current location counter is then used as the address value in a new key-value pair.
The processor never sees the labels: they do not execute and they do not occupy any space in the code or data. The processor sees only machine code instructions, which on MIPS are all 32-bits wide. Each machine code instruction is divided into fields. There are instruction types or formats, which on MIPS are straightforward: I-Type, J-Type, and R-Type. These formats then define the instruction fields, and the assembler follows these encodings. All the instruction formats share the 6-bit opcode field, and this opcode field tells the processor what format the instruction is, which fields it therefore has, and thus how to interpret and execute the rest of the instruction.
The assembler removes labels from the assembly — labels and their names do not exist in the program binary. The label definitions themselves (label:) are omitted from the program binary but usages of labels are translated into numbers, so a machine code instruction that uses a label will have some instruction field that is numeric, and the assembler will provide a proper value for that numeric field so that the effect of the reaching or otherwise accessing what the label referred to is accomplished. (The label is no longer in the program binary, but the code or data memory that the label referred does remain).
The assembler sets up branch instructions, j instructions, and la/lw instructions, using numbers that tell the processor how far forward or backward to move the program counter, or, what address some data of interest is at. The lw/la instructions access data, and these use 2 x 32-bit instructions each holding 16 bits of the address of interest. Between the two instructions, they put together a full 32-bit address for data access. For branches to fully reach any 32-bit address, they would have to put together the 32-bit address in a similar manner (two instruction pair) and use an indirect/register branch.
I have an assignment that requires us to write two functions in MIPS: One that determines if a number is prime or not, and another that finds all the primes between 3 and 102 inclusive and prints them out. Obviously, the second one uses the first; this is meant to be an exercise in jumping and branching. The source code we are given already cleans up the stack and registers, etc., for us, and provides a function to print the number in $a0, so all we need to do is the aforementioned functions. No matter what I do, my program will always just print "1" and exit, even though it should be looping over many numbers. I feel like the mistake I have made is simple, like not loading anything into a register that needs it or overlooking something similarly simple, but I'm still new to MIPS and debugging assembly code is honestly a nightmare. Here are the two functions I've written:
Here's the one for checking if a number is prime:
ori $s0,$a0,0 #make a copy of the argument
ori $t0,$s0,0 #using temp registers is good
ori $s1,$s0,0 #make another copy to use as the loop-end check
li $t2,2 #use 2 as the divisor, makes it easier to check
li $v0,0 #using "not prime" as the base case
prime_test:
slt $t1,$t2,$s1 #check if we're above the ceiling
beq $t1,$zero,found_prime
div $t0,$t2 #check if number is divisible by 2
mfhi $t3 #get remainder from hi
beq $t3,$zero,not_prime #if hi=0, it's not a prime
addi $t2,$t2,1
j prime_test
not_prime:
add $v0,$zero,$zero #return false (0) if not prime
found_prime:
jal print_number #if it's prime, print it
And here's the one for looping and checking from 3 to 102:
li $t0,102 #upper boundary
li $a0,3 #lower boundary
prime_loop:
slt $t5,$a0,$t0
beq $t5,$zero,found_all_primes
jal is_prime
slt $t2,$zero,$a0
bne $t2,$zero,prime_detected
prime_detected:
addi $a0,$a0,1 #increment the counter
j prime_loop
found_all_primes:
Honestly, I think the error is probably in the function that loops; I'm pretty sure my logic for testing if a number is prime is sound, but I could have easily overlooked something. Again, don't worry about it looking like registers aren't being restored properly, that's all taken care of already.
I don't know too much about MIPS, because we have to do it next year at university, but, this year we got to work with lex and yacc and ofcourse we need to know MIPS. I just learned something about it few hours ago, but for example if we have 'a=-2' and 'b=-a', I know that for 'a=-2' we have something like that 'addi $1, $0, -2', and for 'b=-a' we have something like that 'move $2, $31'. I understood untill here, but I want to know something. $31 is register where 'b' will be stored? and if yes, what is so special at that register? Why can't be stored in $30 , or $29 for example? It is because $31 is last register?
Register assignment is based upon the compiler's allocation scheme, subject to the mips ABI http://www.cs.uwm.edu/classes/cs315/Bacon/Lecture/HTML/ch05s03.html
So, if you have two variables: a and b, the compiler can assign them to any register that is available for the given purpose. Register $31 aka $ra is the return address register. It's not a good choice to retain a data value because $ra is hardwired into the jal instruction.
$0 aka $zero is hardwired to the value of zero. Other registers can be used for any purpose, but most compilers, and most programs, adhere to the register usage conventions of the ABI.
Thus, $1 aka $at is the "assembler temporary". This is used because mips only has conditional branch instructions for equality/inequality (e.g. beq/bne) and does not have (e.g. blt). So, it has an slt instruction that takes an output register, which is generally the $at register
For your sequence:
a = -2;
b = -a;
Let's assume that a has been assigned $t0 and b has been assigned to $t1. The generated sequence would be:
addi $t0,$zero,-2 # a = -2
sub $t1,$zero,$t0 # b = -a
Also, for more on what can and cannot be done with $ra, see my answer here: Whether $ra register callee saved or caller saved in mips?
$31 register in MIPS is the return address register. It is saved by the calling function. It is available for use after saving.
But there is no checking against that. It can be used in a lw instruction just like any other general purpose register.
What is the use of a $zero register in MIPS?
What does it mean?
lw $t0, myInteger($zero)
The zero register always holds the constant 0. There's not really anything special about it except for the fact that 0 happens to be a very useful constant. So useful that the MIPS designers dedicated a register to holding its value. (This way you don't have to waste another register, or any memory, holding the value.)
EDIT:
As for the question of what that line of code means, it loads the word from MEMORY[myInteger + 0] into the $t0 register. The lw command takes both a constant (myInteger) and a register ($zero). Not sure why that is, but that's just how the instructions work. Since myInteger was used as the constant, a register had to be provided, so $zero was used.
This is probably a simple, obvious thing I'm just not seeing, but how do I load an address in a MIPS64 processor? In a MIPS32 processor the following assembler pseudo-instruction:
la $at, LabelAddr
Expands into:
lui $at, LabelAddr[31:16]
ori $at,$at, LabelAddr[15:0]
Looking at the MIPS64 instruction set, I see that lui still loads a 16-bit immediate into the upper half of a 32-bit word. There doesn't appear to be any kind of expanded instruction that loads an immediate anywhere into the upper area of a 64-bit word. This seems, then, that to do the equivalent of an la pseudo-instruction I'd need to expand into code something like:
lui $at, LabelAddr[63:48]
ori $at, $at, LabelAddr[47:32]
sll $at, 16
ori $at, $at, LabelAddr[31:16]
sll $at, 16
ori $at, $at, LabelAddr[15:0]
This strikes me as a bit ... convoluted for something as basic as loading an address so it leaves me convinced that I've overlooked something.
What is it I've overlooked (if anything)?
I think if you need to load a lot of constants, you should put it in a constant pool (A.K.A "literal pool") near the current code and then load it by an ld instruction.
For example: $s0 contains the pool's base address, and the constant you want to load is at offset 48, you can load it to $t1 by the instruction ld $t1, 48($s0)
This technique is very common in ARM, where instructions could only load a 12-bit immediate (only later versions of ARM can load 16-bit immediates with some restrictions). And it is used in Java too.
However somehow MIPS compilers still always generate multiple instructions to load a 64-bit immediate. For example to load 0xfedcba0987654321 on MIPS gcc uses
li $2,-9568256 # 0xffffffffff6e0000
daddiu $2,$2,23813
dsll $2,$2,17
daddiu $2,$2,-30875
dsll $2,$2,16
daddiu $2,$2,17185
Many other RISC architectures have more efficient ways to load an immediate so they need less instructions, but still at least 4. Maybe the instruction cache cost is lower than data cache cost in those cases, or maybe someone just don't like that idea
Here's an example of handwritten constant pool on MIPS
# load pool base address
dla $s0, pool
foo:
# just some placeholder
addu $t0, $t0, $t1
bar:
# load from pool
ld $a0, pool_foo($s0)
ld $a1, pool_bar($s0)
.section pool
# macro helper to define a pool entry
.macro ENTRY label
pool_entry_\label\(): .quad \label
.equ pool_\label\(), pool_entry_\label - pool
.endm
ENTRY foo
ENTRY bar
I failed to persuade any MIPS compilers to emit a literal pool but here's a compiler-generated example on ARM
address so it leaves me convinced that I've overlooked something.
What is it I've overlooked (if anything)?
What you are missing is that even in Mips64 the instruction size stays 32bit (4bytes). In this 32bit machine code encoding system, The 'la' translated to 'lui' + 'ori' combination can handle a max of 32 bit value (address). There are not enough bits in the 4byte machine instruction to easily encode a 64bit address. To deal with 64bit address, more iterations of the same (lui+ori) is used along with shifts (dsll).
Paxym