Beispiel #1
0
    def test_conditional_instructions_incorrect_prediction_no_commit(self):
        """Test that Conditional Instructions with incorrect predicate do not commit."""
        # Initialize units.
        root = FlushableLog()
        rs = FeedLog()
        lsq = FeedLog()
        rob = ReorderBuffer(self.rf, rs, lsq, capacity=32)
        rob.set_pipeline_flush_root(root)
        rob.WIDTH = 4
        self.rf['r4'] = 0
        self.rf['r5'] = 10

        # Initialize instructions to be fed.
        cond = Blth('r4', 'r5', 100)
        cond.branch_info = BranchInfo(False, 100, 1, None)
        cond.DELAY = 0
        add = AddI('r1', 'r1', 1)
        add.DELAY = 0

        # Feed pairs of (cond, add) instructions.
        n_pairs = 5
        for i in range(n_pairs):
            rob.feed(cond)
            rob.feed(add)
        rob.tick()  # Insert into current queue.

        # Receive result for each pair in turn.
        for i in range(0, n_pairs, 2):
            rob.receive(Result(rs.log[i].tag, True))
            rob.receive(Result(rs.log[i + 1].tag, 0))
            rob.tick()
            self.assertEqual(self.rf['r1'], 0)
        self.assertEqual(self.rf['pc'], 100)
Beispiel #2
0
    def test_conditional_instructions_correct_prediction_commit(self):
        """Test Conditional Instructions with correct predicate commit OK."""
        # Initialize units.
        rs = FeedLog()
        lsq = FeedLog()
        rob = ReorderBuffer(self.rf, rs, lsq, capacity=32)
        rob.WIDTH = 4
        self.rf['r4'] = 0
        self.rf['r5'] = 0

        # Initialize instructions to be fed.
        cond = Blth('r4', 'r5', 2)
        cond.branch_info = BranchInfo(False, 2, 2, None)
        cond.DELAY = 0
        add = AddI('r1', 'r1', 1)
        add.DELAY = 0

        # Feed pairs of (cond, add) instructions.
        n_pairs = 5
        for i in range(n_pairs):
            rob.feed(cond)
            rob.feed(add)
        rob.tick()  # Insert into current queue.

        # Receive result for each pair in turn.
        r1_value = 0
        for i in range(0, n_pairs, 2):
            rob.receive(Result(rs.log[i].tag, False))
            rob.receive(Result(rs.log[i + 1].tag, r1_value))
            rob.tick()
            self.assertEqual(self.rf['r1'], r1_value)
            r1_value += 1
Beispiel #3
0
 def setUp(self):
     self.n_gpr_registers = 31
     self.rf = RegisterFile(self.n_gpr_registers)
     self.memory = Memory(128)
     self.bus = BusLog()
     self.log = FeedLog()
     self.lsq = LoadStoreQueue(self.memory, self.bus, capacity=32)
     self.generate_add = lambda cap: Add(
         'r%d' % random.randint(0, cap - 1), 'r%d' % random.randint(
             0, cap - 1), 'r%d' % random.randint(0, cap - 1))
Beispiel #4
0
 def setUp(self):
     self.rs = ReservationStation(capacity=200)
     self.feed_log = FeedLog()
     self.feed_log.capability = lambda: Instruction
     self.generate_add = lambda: IntegerLogical(
         'ROB1', lambda o1, o2: o1 + o2, random.randint(-100, 100),
         random.randint(-100, 100))
Beispiel #5
0
    def setUp(self):
        self.feed_log = FeedLog()
        # ReorderBuffer full method takes a param. Modify FeedLog to ignore it.
        self.feed_log._full = self.feed_log.full
        self.feed_log.full = lambda x: self.feed_log._full()

        self.test_strs = [('add r1 r2 r3', ins.Add('r1', 'r2', 'r3')),
                          ('addi r1 r2 5', ins.AddI('r1', 'r2', 5)),
                          ('sub rd r0 r9', ins.Sub('rd', 'r0', 'r9')),
                          ('subi r1 r2 10', ins.SubI('r1', 'r2', 10)),
                          ('ldr r0 r1', ins.Load('r0', 'r1')),
                          ('str r99 r100', ins.Store('r99', 'r100')),
                          ('j 1000', ins.Jump(1000)),
                          ('blth r1 r2 10', ins.Blth('r1', 'r2', 10))]
        self.test_strs = [({'instruction_str': ins_str}, exp_ins)
                          for (ins_str, exp_ins) in self.test_strs]
        self.test_strs[7][0]['branch_info'] = BranchInfo(False, 10, 9, 8)
    def init_run(self):
        self.test_program_str = [str(i) for i in TEST_PROGRAM]

        self.feed_log = FeedLog()
        self.feed_log.full = lambda _: False
        self.decode = Decode(self.feed_log)

        self.reg_file = RegisterFile(10)
        self.fetch = Fetch(self.reg_file,
                           self.test_program_str,
                           self.decode,
                           NeverTaken())
Beispiel #7
0
 def test_instructions_removed_from_queue_on_commit(self):
     """Test that commit frees a slot in the ROB."""
     for capacity in [1, 5, 25, 200]:
         log = FeedLog()
         rob = ReorderBuffer(self.rf,
                             log,
                             self.lsq,
                             capacity=capacity,
                             width=4)
         # Half fill.
         for _ in range(capacity // 2):
             rob.feed(self.generate_add(self.n_gpr_registers))
         rob.tick()  # Instructions now in current queue.
         # Remove all fed from ROB queue by giving values.
         self.assertEqual(capacity // 2, len(log.log))
         for ins in log.log:
             rob.receive(Result(ins.tag, 5))
         for _ in range(math.ceil(capacity / rob.width)):
             rob.tick()
         # Should now be able to feed capacity instructions.
         for _ in range(capacity):
             rob.feed(self.generate_add(self.n_gpr_registers))
Beispiel #8
0
 def setUp(self):
     self.reg_file = RegisterFile(10)
     self.feed_log = FeedLog()
     self.branch_predictor = NeverTaken()
Beispiel #9
0
class TestFetch(unittest.TestCase):

    def setUp(self):
        self.reg_file = RegisterFile(10)
        self.feed_log = FeedLog()
        self.branch_predictor = NeverTaken()

    def test_operate(self):
        """Test Fetch feeds correct instruction strings."""
        test_program_str = [str(i) for i in TEST_PROGRAM]

        for width in [1, 2, 3]:
            self.feed_log.reset()
            self.reg_file['pc'] = 0

            fetch = Fetch(self.reg_file,
                          test_program_str,
                          self.feed_log,
                          self.branch_predictor,
                          width=width)

            n_inst = len(TEST_PROGRAM)
            n_fetch = 0
            while n_fetch <= n_inst:
                fetch.tick()
                n_fetch += width
                self.assertListEqual(self.feed_log.log,
                                     [{'instruction_str': ins_str}
                                      for ins_str in test_program_str[:n_fetch]])
                self.assertEqual(self.reg_file['pc'], min(n_fetch, n_inst))
            # PC is now beyond # program instructions. Tick ensures Fetch handles
            # this gracefully.
            fetch.tick()

    def test_conditional_branch_tag(self):
        ins = Blth('r1', 'r2', 10)
        ins_str = str(ins)

        fetch = Fetch(self.reg_file,
                      [ins_str],
                      self.feed_log,
                      self.branch_predictor)
        fetch.tick()

        exp_ins = {'instruction_str': ins_str,
                   'branch_info': BranchInfo(False, 10, 1, 0)}

        self.assertDictEqual(self.feed_log.log[0], exp_ins)

    def test_flush(self):
        """Ensure flush flushes Fetch and Decode."""
        log = FlushableLog()
        self.feed_log.flush = log.flush

        fetch = Fetch(self.reg_file,
                      [],
                      self.feed_log,
                      self.branch_predictor)
        fetch.flush()
        self.assertEqual(log.n_flush, 1, 'Fetch must flush Decode')

    def test_unconditional_branch_set_pc(self):
        """Ensure Fetch detects unconditional branch and sets pc."""
        self.reg_file['pc'] = 0
        fetch = Fetch(self.reg_file,
                      ['j 100'],
                      self.feed_log,
                      self.branch_predictor)
        fetch.tick()
        fetch.tick()
        self.assertEqual(self.reg_file['pc'], 100,
                         'Fetch should detect unconditional branches and set pc')
Beispiel #10
0
class TestReorderBuffer(unittest.TestCase):
    def setUp(self):
        self.n_gpr_registers = 31
        self.rf = RegisterFile(self.n_gpr_registers)
        self.memory = Memory(128)
        self.bus = BusLog()
        self.log = FeedLog()
        self.lsq = LoadStoreQueue(self.memory, self.bus, capacity=32)
        self.generate_add = lambda cap: Add(
            'r%d' % random.randint(0, cap - 1), 'r%d' % random.randint(
                0, cap - 1), 'r%d' % random.randint(0, cap - 1))

    def test_invalid_capacity(self):
        """Test exception thrown when initialized with invalid capacity."""
        for _ in range(100):
            invalid = random.randint(-1000, 0)
            with self.assertRaises(ValueError):
                ReorderBuffer(self.rf, self.log, self.lsq, capacity=invalid)

    def test_feed_full(self):
        """Test full operation when feeding Instructions."""
        for rob_capacity in [1, 5, 25, 200]:
            for rs_capacity in [1, 5, 25, 200]:
                for lsq_capacity in [1, 5, 25, 200]:
                    register_limit = min(rob_capacity, rs_capacity)
                    memory_limit = min(rob_capacity, lsq_capacity)

                    rs = ReservationStation(capacity=rs_capacity)
                    lsq = LoadStoreQueue(self.memory,
                                         self.bus,
                                         capacity=lsq_capacity)
                    rob = ReorderBuffer(self.rf,
                                        rs,
                                        lsq,
                                        capacity=rob_capacity)

                    for _ in range(register_limit):
                        ins = self.generate_add(self.n_gpr_registers)
                        self.assertFalse(
                            rob.full(ins),
                            'ReorderBuffer should not be full after < %d feeds'
                            % register_limit)
                        rob.feed(ins)
                    self.assertTrue(
                        rob.full(self.generate_add(self.n_gpr_registers)),
                        'ReorderBuffer should be full after %d feeds' %
                        register_limit)
                    with self.assertRaises(AssertionError):
                        rob.feed(self.generate_add(self.n_gpr_registers))

    def test_get_queue_id(self):
        """Test that _get_queue_id throws an error on wrap-around."""
        for capacity in [1, 5, 25, 200]:
            rob = ReorderBuffer(self.rf, self.log, self.lsq, capacity=capacity)
            for _ in range(capacity):
                rob._get_queue_id()
            with self.assertRaises(AssertionError):
                rob._get_queue_id()

    def test_instructions_removed_from_queue_on_commit(self):
        """Test that commit frees a slot in the ROB."""
        for capacity in [1, 5, 25, 200]:
            log = FeedLog()
            rob = ReorderBuffer(self.rf,
                                log,
                                self.lsq,
                                capacity=capacity,
                                width=4)
            # Half fill.
            for _ in range(capacity // 2):
                rob.feed(self.generate_add(self.n_gpr_registers))
            rob.tick()  # Instructions now in current queue.
            # Remove all fed from ROB queue by giving values.
            self.assertEqual(capacity // 2, len(log.log))
            for ins in log.log:
                rob.receive(Result(ins.tag, 5))
            for _ in range(math.ceil(capacity / rob.width)):
                rob.tick()
            # Should now be able to feed capacity instructions.
            for _ in range(capacity):
                rob.feed(self.generate_add(self.n_gpr_registers))

    def test_inorder_commit(self):
        """Ensure instruction Results are committed in-order."""
        for _ in range(30):
            for capacity in [1, 5, 25, 200]:
                # Initialize test components.
                self.log.reset()
                zeros = {'r%d' % i: 0 for i in range(capacity)}
                act_rf = RegisterFile(capacity, init_values=zeros)
                exp_rf = RegisterFile(capacity, init_values=zeros)
                width = random.randint(1, 2 * capacity)
                rob = ReorderBuffer(act_rf,
                                    self.log,
                                    self.lsq,
                                    capacity=capacity,
                                    width=width)

                # Feed instructions into ROB.
                n_ins = random.randint(1, capacity)
                register_names = []
                for i in range(n_ins):
                    add = self.generate_add(capacity)
                    register_names.append(add.rd)
                    rob.feed(add)
                rob.tick()

                # Generate a Result value for each fed instruction.
                result_vals = [random.randint(1, 10000) for _ in range(n_ins)]

                # Publish all but first result in reverse order to ROB. Should be
                # no updates to act_rf as the first instruction is stalled!
                for i in reversed(range(1, n_ins)):
                    rob.receive(Result(self.log.log[i].tag, result_vals[i]))
                    rob.tick()
                    self.assertEqual(exp_rf, act_rf)

                # Publish result of first instruction - all can now be comitted in
                # turn.
                rob.receive(Result(self.log.log[0].tag, result_vals[0]))

                # Group updates into ROB width chunks.
                updates = list(zip(register_names, result_vals))
                group_updates = [
                    updates[i:i + rob.width]
                    for i in range(0, len(updates), rob.width)
                ]

                # Ensure in-order commit of width instructions per tick.
                for group in group_updates:
                    rob.tick()
                    for (name, result) in group:
                        exp_rf[name] = result
                    self.assertEqual(exp_rf, act_rf)
                rob.tick()

    def test_conditional_instructions_correct_prediction_commit(self):
        """Test Conditional Instructions with correct predicate commit OK."""
        # Initialize units.
        rs = FeedLog()
        lsq = FeedLog()
        rob = ReorderBuffer(self.rf, rs, lsq, capacity=32)
        rob.WIDTH = 4
        self.rf['r4'] = 0
        self.rf['r5'] = 0

        # Initialize instructions to be fed.
        cond = Blth('r4', 'r5', 2)
        cond.branch_info = BranchInfo(False, 2, 2, None)
        cond.DELAY = 0
        add = AddI('r1', 'r1', 1)
        add.DELAY = 0

        # Feed pairs of (cond, add) instructions.
        n_pairs = 5
        for i in range(n_pairs):
            rob.feed(cond)
            rob.feed(add)
        rob.tick()  # Insert into current queue.

        # Receive result for each pair in turn.
        r1_value = 0
        for i in range(0, n_pairs, 2):
            rob.receive(Result(rs.log[i].tag, False))
            rob.receive(Result(rs.log[i + 1].tag, r1_value))
            rob.tick()
            self.assertEqual(self.rf['r1'], r1_value)
            r1_value += 1

    def test_conditional_instructions_incorrect_prediction_no_commit(self):
        """Test that Conditional Instructions with incorrect predicate do not commit."""
        # Initialize units.
        root = FlushableLog()
        rs = FeedLog()
        lsq = FeedLog()
        rob = ReorderBuffer(self.rf, rs, lsq, capacity=32)
        rob.set_pipeline_flush_root(root)
        rob.WIDTH = 4
        self.rf['r4'] = 0
        self.rf['r5'] = 10

        # Initialize instructions to be fed.
        cond = Blth('r4', 'r5', 100)
        cond.branch_info = BranchInfo(False, 100, 1, None)
        cond.DELAY = 0
        add = AddI('r1', 'r1', 1)
        add.DELAY = 0

        # Feed pairs of (cond, add) instructions.
        n_pairs = 5
        for i in range(n_pairs):
            rob.feed(cond)
            rob.feed(add)
        rob.tick()  # Insert into current queue.

        # Receive result for each pair in turn.
        for i in range(0, n_pairs, 2):
            rob.receive(Result(rs.log[i].tag, True))
            rob.receive(Result(rs.log[i + 1].tag, 0))
            rob.tick()
            self.assertEqual(self.rf['r1'], 0)
        self.assertEqual(self.rf['pc'], 100)

    def test_flush(self):
        """Ensure flush of ReorderBuffer, LoadStoreQueue and ReservationStation."""
        rs = FlushableLog()
        rs.full = lambda: False
        rs.feed = lambda x: None
        lsq = FlushableLog()
        lsq.full = lambda: False
        lsq.feed = lambda x: None

        for rob_capacity in [1, 5, 25, 200]:
            for rs_capacity in [1, 5, 25, 200]:
                for lsq_capacity in [1, 5, 25, 200]:
                    register_limit = min(rob_capacity, rs_capacity)

                    rs.reset()
                    lsq.reset()
                    rob = ReorderBuffer(self.rf,
                                        rs,
                                        lsq,
                                        capacity=rob_capacity)

                    for _ in range(register_limit):
                        rob.feed(self.generate_add(self.n_gpr_registers))
                    rob.flush()
                    self.assertFalse(
                        rob.full(self.generate_add(self.n_gpr_registers)),
                        'ReorderBuffer should not be full after flush')
                    self.assertEqual(
                        rs.n_flush, 1,
                        'ReorderBuffer must flush ReservationStation')
                    self.assertEqual(
                        lsq.n_flush, 1,
                        'ReorderBuffer must flush LoadStoreQueue')
Beispiel #11
0
class TestDecode(unittest.TestCase):

    def setUp(self):
        self.feed_log = FeedLog()
        # ReorderBuffer full method takes a param. Modify FeedLog to ignore it.
        self.feed_log._full = self.feed_log.full
        self.feed_log.full = lambda x: self.feed_log._full()

        self.test_strs = [('add r1 r2 r3', ins.Add('r1', 'r2', 'r3')),
                          ('addi r1 r2 5', ins.AddI('r1', 'r2', 5)),
                          ('sub rd r0 r9', ins.Sub('rd', 'r0', 'r9')),
                          ('subi r1 r2 10', ins.SubI('r1', 'r2', 10)),
                          ('ldr r0 r1', ins.Load('r0', 'r1')),
                          ('str r99 r100', ins.Store('r99', 'r100')),
                          ('j 1000', ins.Jump(1000)),
                          ('blth r1 r2 10', ins.Blth('r1', 'r2', 10))]
        self.test_strs = [({'instruction_str': ins_str}, exp_ins)
                          for (ins_str, exp_ins) in self.test_strs]
        self.test_strs[7][0]['branch_info'] = BranchInfo(False, 10, 9, 8)

    def test_correct_result(self):
        """Test correct Result computed and fed by Decode stage."""
        for delay in [1, 2, 5, 10]:
            self.feed_log.reset()
            unit = decode.Decode(self.feed_log)
            unit.DELAY = delay
            exp_log = []
            self.feed_log.log = []
            for (act_ins, exp_ins) in self.test_strs:
                unit.feed(act_ins)
                unit.tick() # Fed ins_str becomes current state.
                for _ in range(unit.DELAY):
                    # Check Instruction not output before DELAY ticks.
                    self.assertTrue(instruction_list_equal(self.feed_log.log, exp_log))
                    unit.tick()
                exp_log.append(exp_ins)
                if isinstance(exp_ins, ins.Blth):
                    exp_log[-1].branch_info = act_ins['branch_info']
                self.assertTrue(instruction_list_equal(self.feed_log.log, exp_log))

    def test_full(self):
        """Test Decode full method updates correctly after ticks."""
        test_ins = self.test_strs[0][0]

        for capacity in [1, 5, 10]:
            for delay in [1, 2, 5, 10]:
                unit = decode.Decode(self.feed_log, capacity=capacity)
                unit.DELAY = delay

                self.assertFalse(unit.full(),
                                 'Decode should not be full after initialization')
                for _ in range(capacity):
                    unit.feed(test_ins)
                self.assertTrue(unit.full(),
                                'Decode should be full after being fed')
                for i in range(unit.DELAY):
                    unit.tick()
                    self.assertTrue(unit.full(),
                                    'Decode should be full before DELAY ticks %r %r')
                unit.tick()
                self.assertFalse(unit.full(),
                                 'Decode should not be full after DELAY ticks')

    def test_decode_str(self):
        for act_ins, exp_ins in self.test_strs:
            if isinstance(exp_ins, ins.Blth):
                exp_ins.branch_info = act_ins['branch_info']
            self.assertTrue(instruction_list_equal([decode._decode(act_ins)],
                                                   [exp_ins]))

    def test_flush(self):
        """Ensure flush flushes Decode and ReorderBuffer."""
        log = FlushableLog()
        self.feed_log.flush = log.flush

        unit = decode.Decode(self.feed_log, capacity=1)

        unit.feed(self.test_strs[0][0])
        self.assertTrue(unit.full(),
                        'Decode should be full after being fed')
        unit.flush()
        self.assertFalse(unit.full(),
                         'Decode should not be full after flush')
        self.assertEqual(log.n_flush, 1, 'Decode must flush ReorderBuffer')

    def test_superscalar(self):
        """Ensure up to width instructions are issued per cycle."""
        for capacity in [1, 5, 10, 200]:
            for width in [1, 4, 16]:

                for delay in [1, 5, 10]:
                    self.feed_log.reset()
                    unit = decode.Decode(self.feed_log, capacity=capacity, width=width)
                    unit.DELAY = delay

                    exp_ins = []
                    for i in range(random.randint(1, 100)):
                        act, exp = self.test_strs[i % (len(self.test_strs) - 1)]
                        if not unit.full():
                            unit.feed(act)
                        if i < min(capacity, width):
                            exp_ins.append(exp)

                    unit.tick()
                    # Issue all fed.
                    for _ in range(delay):
                        unit.tick()

                    self.assertTrue(instruction_list_equal(self.feed_log.log, exp_ins))

    def test_halt(self):
        """Ensure Halt is decoded."""
        unit = decode.Decode(self.feed_log, capacity=12)
        unit.feed({'instruction_str': 'halt'})
        unit.tick()
        unit.tick()
        self.assertTrue(instruction_list_equal(self.feed_log.log, [ins.Halt()]))