class PipelineInterface(object):
    def __init__(self, instruction_list, starting_pc_address, register_memory,
                 data_mem):
        """
        Creates the interface to the pipeline.
        :param instruction_list: list of Instruction objects in the order they should appear in instruction memory
        :param starting_pc_address: integer starting address for the program counter. This will also be the base
        address of instruction memory
        :param register_memory: list representing register memory, since the registers are just reg0,reg1, etc
        :param data_mem: dictionary representing data memory
        """
        self.fetch = Fetch(instruction_list, starting_pc_address)

        self.write_back = WriteBack(fetch_stage=self.fetch)
        self.memory = Memory(data_mem, self.write_back)
        self.execute = Execute(self.memory)
        self.decode = Decode(register_memory, self.execute)
        self.write_back.decode_stage = self.decode

    def trigger_clock_cycle(self):
        """
        For single-cycle, we really only do something useful in the fetch.on_rising_clock. It will call a method of the
        next stage that will do what that stage needs to and then call the next stage, etc
        In pipelined execution, these would be used because Fetch would fetch the instruction, place it in the
        intermediate register, and then return back to here, where decode.on_rising_clock would be called.
        :return:
        """
        self.fetch.on_rising_clock(self.decode)
        self.decode.on_rising_clock()
        self.execute.on_rising_clock()
        self.memory.on_rising_clock()
        self.write_back.on_rising_clock()

    def retrieve_register_list(self):
        """
        Helper method to return the current register file for comparing that the instruction was written back
        successfully
        :return: list representing register file
        """
        return self.decode.register_file

    def retrieve_data_memory(self):
        """
        Helper method to return the current data memory dictionary to help ensure that the data was written to the
        correct place
        :return: memory dictionary
        """
        return self.memory.memory

    def retrieve_current_pc_address(self):
        """
        Helper method to retrieve the current value of the program counter from the fetch stage.
        :return: int (probably)
        """
        return self.fetch.program_counter

    def retrive_instruction_name(self):
        return self.decode.instruction.asm_version()
    def __init__(self, instruction_list, starting_pc_address, register_memory,
                 data_mem):
        """
        Creates the interface to the pipeline.
        :param instruction_list: list of Instruction objects in the order they should appear in instruction memory
        :param starting_pc_address: integer starting address for the program counter. This will also be the base
        address of instruction memory
        :param register_memory: list representing register memory, since the registers are just reg0,reg1, etc
        :param data_mem: dictionary representing data memory
        """
        self.fetch = Fetch(instruction_list, starting_pc_address)

        self.write_back = WriteBack(fetch_stage=self.fetch)
        self.memory = Memory(data_mem, self.write_back)
        self.execute = Execute(self.memory)
        self.decode = Decode(register_memory, self.execute)
        self.write_back.decode_stage = self.decode
    def test_receive_values_and_update_addi(self):
        execute = Execute()
        expected_result = create_sized_binary_num(
            decode_signed_binary_number(ExecuteStageTest.data1, 32) +
            decode_signed_binary_number(ExecuteStageTest.immediate, 32),
            32)[:32]

        ALUOp = 0b11
        function_field = '100000'
        opcode = create_sized_binary_num(8, 6)
        ALUSrc = True
        MemWrite = False
        MemtoReg = False
        MemRead = False
        Branch = False
        jump = False
        jump_address = None

        execute.receive_data(ExecuteStageTest.data1, ExecuteStageTest.data2,
                             ExecuteStageTest.immediate,
                             ExecuteStageTest.pc_value, jump_address)
        execute.receive_control_information(ALUOp, function_field, opcode,
                                            ALUSrc, MemWrite, MemtoReg,
                                            MemRead, Branch, jump)
        self.assertEqual(expected_result, execute.alu_output)
    def test_branch_if_not_equal_true(self):
        execute = Execute()
        alu_branch_output = True
        branch_address = create_sized_binary_num(
            ExecuteStageTest.pc_value +
            (decode_signed_binary_number(ExecuteStageTest.immediate, 32) << 2),
            32)[:32]

        ALUOp = 0b01
        function_field = create_sized_binary_num(4, 6)
        opcode = '000101'
        ALUSrc = False
        MemWrite = False
        MemtoReg = False
        MemRead = False
        Branch = True
        jump = False
        jump_address = None

        execute.receive_data(ExecuteStageTest.data1, ExecuteStageTest.data2,
                             ExecuteStageTest.immediate,
                             ExecuteStageTest.pc_value, jump_address)
        execute.receive_control_information(ALUOp, function_field, opcode,
                                            ALUSrc, MemWrite, MemtoReg,
                                            MemRead, Branch, jump)
        self.assertEqual(alu_branch_output, execute.alu_branch)
        self.assertEqual(branch_address, execute.branch_address)
 def test_create_execute_object_default_values(self):
     execute = Execute()
     self.assertEqual(None, execute.ALUOp)
     self.assertEqual(None, execute.ALUSrc)
     self.assertEqual(None, execute.function_code)
     self.assertEqual(None, execute.opcode)
     self.assertEqual(None, execute.immediate)
     self.assertEqual(None, execute.read_data1)
     self.assertEqual(None, execute.read_data2)
     self.assertEqual(None, execute.operation)
     self.assertEqual(False, execute.branch_equal)
     self.assertEqual(False, execute.branch_not_equal)
     self.assertEqual(None, execute.alu_input_1)
     self.assertEqual(None, execute.alu_input_2)
     self.assertEqual(None, execute.alu_output)
     self.assertEqual(None, execute.alu_branch)
    def test_slt_set(self):
        execute = Execute()
        expected_result = create_sized_binary_num(1, 32)[:32]

        ALUOp = 0b10
        function_field = create_sized_binary_num(42, 6)
        opcode = None
        ALUSrc = False
        MemWrite = False
        MemtoReg = False
        MemRead = False
        Branch = False
        jump = False
        jump_address = None

        execute.receive_data(ExecuteStageTest.data2, ExecuteStageTest.data1,
                             ExecuteStageTest.immediate,
                             ExecuteStageTest.pc_value, jump_address)
        execute.receive_control_information(ALUOp, function_field, opcode,
                                            ALUSrc, MemWrite, MemtoReg,
                                            MemRead, Branch, jump)
        self.assertEqual(expected_result, execute.alu_output)