Project

General

Profile

Actions

Язык описания шаблонов тестовых программ » History » Revision 81

« Previous | Revision 81/89 (diff) | Next »
Alexander Kamkin, 10/05/2011 04:32 PM


Язык Ruby-MT описания шаблонов тестовых программ

Язык Ruby-MT (Ruby for MicroTESK) предназначен для компактного и переиспользуемого описания функциональных тестов для микропроцессоров и других программируемых устройств. Язык представляет собой смесь языка ассемблера целевого микропроцессора (TL, Target Language) и управляющего языка высокого уровня (ML, Meta Language). При этом ML можно рассматривать как макропроцессор, поскольку в результате выполнения его конструкций генерируется текст на TL. Язык построен на основе Ruby в форме библиотечного расширения (не требуется дополнительных парсеров и т.п.).

Код на Ruby-MT описывает шаблон тестовой программы (далее для краткости шаблон). Обработка шаблона состоит из следующих шагов:

  1. Препроцессирование шаблона.
  2. Выполнение шаблона.

При выполнении шаблона, он генерирует программу путем обращения к базе данных ограничений, солверам и другим стандартным компонентам генератора через API генератора.

Интуитивное описание языка на примерах

Пример 1 (MIPS)

class MyTemplate < MipsTemplate
    # В базовом шаблоне содержатся общие инструкции инициализации микропроцессора
    def initialize()
        super()
    end

    # Главный метод теста
    def test()
        # Повторить в тестовой программе 100 раз следующую ситуацию
        100.times {
            # Добавление в тестовую программу комментария
            text(''# --------------------------------------------------------------------------------'');
            # Загрузка в регистр reg1 содержимого памяти по адресу, содержащемуся в регистре base1
            # Функция r выделяет новый регистр общего назначения
            ld reg1=r, 0x0(base1=r)  ;; goal([l1Hit,50],[!l1Hit,50])        # Попадание в кэш-память L1 осуществляются с вероятностью 50%
            # Загрузка в регистр reg2 содержимого памяти по адресу, содержащемуся в регистре base2
            ld reg2=r, 0x0(base2=r)  ;; goal(l1Hit && !equal(base1, base2)) # Адреса первой и второй интрукции загрузки не должны совпадать ([base1] != [base2])
            # Запись результата сложения содержимого регистров reg1 и reg2 в регистр res
            dadd res=r, reg1, reg2   ;; goal(!integerOverflow)              # При сложении не должно возникать переполнения
        }
    end

    # В базовом шаблоне содержатся общие инструкции завершения работы микропроцессора
    def finalize()
        super.finalize()
    end
end

В результате обработки этого шаблона будет сгенерирована программа следующего вида.

...
# --------------------------------------------------------------------------------
ld reg001_1, 0x0(base001_1)
ld reg001_2, 0x0(base001_2)
dadd res001, reg001_1, reg001_2
...
# --------------------------------------------------------------------------------
ld reg100_1, 0x0(base100_1)
ld reg100_2, 0x0(base100_2)
dadd res100, reg100_1, reg100_2
...

В этой программе regXXX_{1,2}, baseXXX_{1,2} и resXXX - это регистры общего назначения (некоторые из них совпадают друг с другом). В начале программы (возможно, и в некоторых промежуточных точках) располагается управляющий код, инициализирующий регистры и память так, чтобы удовлетворить заданным в шаблоне ограничениям.

TODO: нужно переопределить операции для тестовых ситуаций (!, &&, ||).
TODO: пока можно ограничиться случаем одной тестовой ситуации в goal().

Распределение регистров

Для каждого типа регистров (GPR, FPR и т.п.) определена функция распределения регистров (например, функция r в примере выше). Эта функция имеет один целочисленный параметр - номер регистра. Если при вызове функции распределения регистров параметр не указан, функция выделяет регистр согласно некоторой предопределенной стратегии распределения регистров. Например, она может возвращать один из не занятых регистров (естественно, возвращаемый регистр помечается как занятый). Поскольку регистров конечное число, не исключены случаи, когда все регистры заняты. В таких ситуациях логично выделять регистры, которые давно не использовались и попутно печатать предупреждение о нехватке регистров. Кроме того, предусмотрена функция освобождения занятых регистров free. При выходе из блока занятые в этом блоке регистры автоматически освобождаются.

Для распределения регистров в программе используются следующие правила:

1. Если в качестве регистра в шаблоне используется конкретный регистр, а не переменная, то этот регистр используется и в сгенерированной программе.

Например, для шаблона

ori @r, r0, 0x0

второй регистр инструкции ori (регистр r0) фиксирован. Примером программы, соответствующей этому шаблону является

ori r7, r0, 0x0 # Регистр r0 фиксирован

2. Если имена переменных, обозначающих регистры, в шаблоне совпадают (в пределах одной области видимости), то в соответствующих частях итоговой программы будет использоваться один и тот же регистр.

Например, для шаблона

2.times {
    add @r1, @r2, @r3
    sub @r4, @r1, @r5 # Результат сложения используется в качестве вычитаемого
}

первый регистр инструкции add всегда будет совпадать со вторым регистром инструкции sub, хотя эти регистры могут быть разными на разных итерациях:

# --------------------------------------------------------------------------------
add r2,  r10, r5
sub r9,  r2,  r15 # Зависимость по регистру r2
# --------------------------------------------------------------------------------
add r11, r27, r9
sub r9,  r11, r23 # Зависимость по регистру r11

Более сложный пример

ori @r1, r0, 0x0
2.times {
    add @r1, @r2, @r3 # Результат сложения заносится в тот же регистр, что и результат вышестоящей инструкции
    sub @r4, @r1, @r5 # Результат сложения используется в качестве вычитаемого
}

В этот случае регистры @r1 на первой и второй итерациях будут совпадать между собой и будут равны первому регистру инструкции ori.

А следующем примере никакой зависимости по регистрам нет, поскольку области видимости переменных-регистров не пересекаются:

2.times {
    add @r1, @r2, @r3
}
2.times {
    add @r1, @r2, @r3 # Зависимости по регистрам нет
}

3. Если имена переменных, обозначающих регистры, в шаблоне различаются, либо переменные находятся в разных областях видимости, либо для обозначения регистра используется символ *, то в соответствующих частях итоговой программы возможны совпадающие регистры.

Регистров ограниченное число, поэтому пересечения по регистрам неизбежны. По возможности, внутри одного блока используются разные регистры. Стратегию распределения регистров нужно обдумать.

Updated by Alexander Kamkin almost 9 years ago · 81 revisions