Rearrange data bypass to have Decode stage
pavelkryukov opened this issue · 3 comments
@denislos Why do we need that backward port?
EDIT: Discussion below proposes that backward port may be removed by adding the Decode stage into the bypass module.
For description of bypasses implementation, check our Wiki page: https://github.com/MIPT-ILab/mipt-mips/wiki/Data-Bypass-and-Scoreboard
In a nutshell, I implemented this port to avoid using Decode stage in the RegisterStage class.
It might look like an unequal exchange, however, there are some disadvantages of having Decode stage as the register stage, which, in my opinion, should be taken into account. And as for advantages, the port will be completely gone, if we add Decode stage.
Disadvantages:
-
It would affect a perception of the RegisterStage class conceived as a characteristic of the execution part of pipeline. Furthermore, we do not actually need Decode stage there, for this specific application is the only one I can see so far.
-
Out-of-context usage (e.g. unit tests) would become more subtle and verbose, IMHO. For instance,
Decode stage is not in the RegisterStage class:
DataBypass db( 3); MIPS32Instr load("ld); load->dst = MIPSRegister::from_cpu_index( 1); MIPS32Instr add("add); add->src = MIPSRegister::from_cpu_index( 1); db.trace_new_instr( load); // load is in EXE_0 (first execution stage) now CHECK( db.is_stall( add)); // stall (src is not ready + retirement at the same time)
Decode stage is in the RegisterStage class:
DataBypass db( 3); MIPS32Instr load("ld); load->dst = MIPSRegister::from_cpu_index( 1); MIPS32Instr add("add); add->src = MIPSRegister::from_cpu_index( 1); db.trace_new_instr( load); // load is in Decode stage now db.update(); // load has been moved to the first execution stage (EXE_0) CHECK( db.is_stall( add)); // stall (src is not ready + retirement at the same time)
It would affect a perception of the RegisterStage class conceived as a characteristic of the execution part of pipeline.
I suppose that's partly true. On Decode stage (at the current implementation), we read the registers; and that may be considered as a part of execution pipeline. If we split the stage into two parts, we would get Decode stage and Allocation stage here, and register read would stay at Allocation.
Out-of-context usage (e.g. unit tests) would become more subtle and verbose,
I generally prefer unit tests to be verbose :-).
In your example:
db.trace_new_instr( load); // load is in Decode stage now
db.update(); // load has been moved to the first execution stage (EXE_0)
CHECK( db.is_stall( add)); // stall (src is not ready + retirement at the same time)
Do I understand correctly that db.update()
serves as a clocking function for db
? In other words. everything before it is assumed to happen on cycle N, while CHECK
is performed on cycle N + 1, right?
Do I understand correctly that
db.update()
serves as a clocking function fordb
? In other words. everything before it is assumed to happen on cycle N, whileCHECK
is performed on cycle N + 1, right?
Yes