Project

General

Profile

Actions

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

« Previous | Revision 63/89 (diff) | Next »
Alexander Kamkin, 10/03/2011 11:56 AM


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

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

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

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

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

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

Пример 1 (MIPS)

template MyTemplate {
    # Повторить в тестовой программе 100 раз следующую ситуацию
    100.times {
        # Загрузка в регистр @Reg1 содержимого памяти по адресу, содержащемуся в регистре @Base1
        ld @Reg1, 0x0(@Base1)   @ choice{L1Hit:50}                         # Попадание в кэш-память L1 осуществляются с вероятностью 50%
        # Загрузка в регистр Reg2 содержимого памяти по адресу, содержащемуся в регистре Base2
        ld @Reg2, 0x0(@Base2)   @ choice{L1Hit:50} && [@Base1] != [@Base2] # Адреса первой и второй интрукции загрузки не должны совпадать
        # Запись результата сложения содержимого регистров @Reg1 и @Reg2 в регистр @Res
        dadd @Res, @Reg1, @Reg2 @ !IntegerOverflow                         # При сложении не должно возникать переполнения
    }
}

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

...
# --------------------------------------------------------------------------------
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 - это регистры общего назначения (некоторые из них совпадают друг с другом). В начале программы (возможно, и в некоторых промежуточных точках) располагается управляющий код, инициализирующий регистры и память так, чтобы удовлетворить заданным в шаблоне ограничениям (это наиболее интеллектуальная часть обработки шаблона).

Формализованное описание языка

Шаблон - это последовательность операторов.

Template ::= "template" Identifier "(" (Parameter ("," Parameter)*)? ")" "{" (Statement)* "}" 

Операторы делятся на два класса: реальные операторы (операторы, которые порождают код) и мета операторы (операторы, которые используются для управления генерацией кода).

Statement ::= RealStatement | MetaStatement
InstructionStatement ::= (Instruction | InstructionClass) (@ Situation)?

Формат инструкции зависит от ассемблера. Обычно он имеет следующий вид:

ConcreteInstruction ::= Identifier (Parameter (, Parameter)+)?
InstructionClass ::= Identifier (Parameter (, Parameter)+)?
MetaStatement ::= MetaVariableDeclaration |
                  MetaVariableAssignment  |
                  MetaIfStatement         |
                  MetaForStatement        |
                  MetaWhileStatement      |
                  ...

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

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

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 about 13 years ago · 89 revisions