LTL свойства и программа Promela

У меня есть следующая программа, которая моделирует FIFO с процессом в PROMELA:

mtype = { PUSH, POP, IS_EMPTY, IS_FULL };

#define PRODUCER_UID 0
#define CONSUMER_UID 1

proctype fifo(chan inputs, outputs)
{
    mtype command;
    int data, tmp, src_uid;
    bool data_valid = false;

    do
        :: true ->
            inputs?command(tmp, src_uid);
            if
                :: command == PUSH ->
                    if
                        :: data_valid ->
                            outputs!IS_FULL(true, src_uid);
                        :: else ->
                            data = tmp
                            data_valid = true;
                            outputs!PUSH(data, src_uid);
                    fi
                :: command == POP ->
                    if
                        :: !data_valid ->
                            outputs!IS_EMPTY(true, src_uid);
                        :: else ->
                            outputs!POP(data, src_uid);
                            data = -1;
                            data_valid = false;
                    fi
                :: command == IS_EMPTY ->
                    outputs!IS_EMPTY(!data_valid, src_uid);
                :: command == IS_FULL ->
                    outputs!IS_FULL(data_valid, src_uid);
            fi;
    od;
}

proctype producer(chan inputs, outputs)
{
    mtype command;
    int v;

    do
        :: true ->
            atomic {
                inputs!IS_FULL(false, PRODUCER_UID) ->
                outputs?IS_FULL(v, PRODUCER_UID);
            }
            if
                :: v == 1 ->
                    skip
                :: else ->
                    select(v: 0..16);
                    printf("P[%d] - produced: %d\n", _pid, v);
access_fifo:
                    atomic {
                        inputs!PUSH(v, PRODUCER_UID);
                        outputs?command(v, PRODUCER_UID);
                    }
                    assert(command == PUSH);
            fi;
    od;
}

proctype consumer(chan inputs, outputs)
{
    mtype command;
    int v;

    do
        :: true ->
            atomic {
                inputs!IS_EMPTY(false, CONSUMER_UID) ->
                outputs?IS_EMPTY(v, CONSUMER_UID);
            }
            if
                :: v == 1 ->
                    skip
                :: else ->
access_fifo:
                    atomic {
                        inputs!POP(v, CONSUMER_UID);
                        outputs?command(v, CONSUMER_UID);
                    }
                    assert(command == POP);
                    printf("P[%d] - consumed: %d\n", _pid, v);
            fi;
    od;
}

init {
    chan inputs  = [0] of { mtype, int, int };
    chan outputs = [0] of { mtype, int, int };

    run fifo(inputs, outputs);     // pid: 1
    run producer(inputs, outputs); // pid: 2
    run consumer(inputs, outputs); // pid: 3
}

я хочу добавить wr_ptr а также rd_ptr в программе для указания записи и чтения указателей относительно глубины FIFO, когда PUSH Обновление выполняется:

wr_ptr = wr_ptr % depth;
empty=0;
if
    :: (rd_ptr == wr_ptr) -> full=true; 
fi

и аналогичные шансы на POP обновления

Не могли бы вы помочь мне добавить это в эту программу?

или я должен сделать это свойство ltl и использовать это, чтобы проверить это?


из комментариев: и я хочу проверить это свойство, например, если fifo заполнен, у него не должно быть запроса на запись, то есть правильный синтаксис?full означает, что fifo заполнен, а wr_idx - указатель записи, я не знаю как получить доступ к полной, пустой, wr_idx, rd_idx, глубине процесса fifo в свойствах ltl fifo_no_write_when_full {[] (full ->! wr_idx)}

1 ответ

Вот пример процесса FIFO с размером 1 что я дал вам здесь адаптированный для произвольного размера, который может быть настроен с FIFO_SIZE, В целях проверки я хотел бы сохранить это значение как можно меньше (например, 3), потому что в противном случае вы просто расширяете пространство состояний, не добавляя более существенного поведения.

mtype = { PUSH, POP, IS_EMPTY, IS_FULL };

#define PRODUCER_UID 0
#define CONSUMER_UID 1
#define FIFO_SIZE    10

proctype fifo(chan inputs, outputs)
{
    mtype command;
    int tmp, src_uid;
    int data[FIFO_SIZE];
    byte head = 0;
    byte count = 0;
    bool res;

    do
        :: true ->
            inputs?command(tmp, src_uid);
            if
                :: command == PUSH ->
                    if
                        :: count >= FIFO_SIZE ->
                            outputs!IS_FULL(true, src_uid);
                        :: else ->
                            data[(head + count) % FIFO_SIZE] = tmp;
                            count = count + 1;
                            outputs!PUSH(data[(head + count - 1) % FIFO_SIZE], src_uid);
                    fi
                :: command == POP ->
                    if
                        :: count <= 0 ->
                            outputs!IS_EMPTY(true, src_uid);
                        :: else ->
                            outputs!POP(data[head], src_uid);
                            atomic {
                                head = (head + 1) % FIFO_SIZE;
                                count = count - 1;
                            }
                    fi
                :: command == IS_EMPTY ->
                    res = count <= 0;
                    outputs!IS_EMPTY(res, src_uid);
                :: command == IS_FULL ->
                    res = count >= FIFO_SIZE;
                    outputs!IS_FULL(res, src_uid);
            fi;
    od;
}

Без изменений producer, consumer или же init было необходимо:

proctype producer(chan inputs, outputs)
{
    mtype command;
    int v;

    do
        :: true ->
            atomic {
                inputs!IS_FULL(false, PRODUCER_UID) ->
                outputs?IS_FULL(v, PRODUCER_UID);
            }
            if
                :: v == 1 ->
                    skip
                :: else ->
                    select(v: 0..16);
                    printf("P[%d] - produced: %d\n", _pid, v);
access_fifo:
                    atomic {
                        inputs!PUSH(v, PRODUCER_UID);
                        outputs?command(v, PRODUCER_UID);
                    }
                    assert(command == PUSH);
            fi;
    od;
}

proctype consumer(chan inputs, outputs)
{
    mtype command;
    int v;

    do
        :: true ->
            atomic {
                inputs!IS_EMPTY(false, CONSUMER_UID) ->
                outputs?IS_EMPTY(v, CONSUMER_UID);
            }
            if
                :: v == 1 ->
                    skip
                :: else ->
access_fifo:
                    atomic {
                        inputs!POP(v, CONSUMER_UID);
                        outputs?command(v, CONSUMER_UID);
                    }
                    assert(command == POP);
                    printf("P[%d] - consumed: %d\n", _pid, v);
            fi;
    od;
}

init {
    chan inputs  = [0] of { mtype, int, int };
    chan outputs = [0] of { mtype, int, int };

    run fifo(inputs, outputs);     // pid: 1
    run producer(inputs, outputs); // pid: 2
    run consumer(inputs, outputs); // pid: 3
}

Теперь у вас должно быть достаточно материала для работы и вы готовы написать свои собственные свойства. По этому поводу в своем вопросе вы пишете:

Я не знаю, как получить доступ к полному, пустому, wr_idx, rd_idx, глубине процесса fifo в свойствах ltl fifo_no_write_when_full {[] (full ->! Wr_idx)}

Прежде всего, обратите внимание, что в моем коде rd_idx соответствует head, depth (должен) соответствовать count и что я не использовал явный wr_idx потому что последний может быть получен из первых двух: он дается (head + count) % FIFO_SIZE, Это не просто выбор чистоты кода, потому что меньшее количество переменных в модели Promela фактически помогает с потреблением памяти и временем выполнения процесса проверки.

Конечно, если вы действительно хотите иметь wr_idx в вашей модели вы можете добавить его самостоятельно. (:

Во-вторых, если вы посмотрите руководство Promela для свойств ltl, вы обнаружите, что:

Имена или символы должны быть определены для представления логических выражений для глобальных переменных из модели.

Другими словами, невозможно поместить локальные переменные в выражение ltl. Если вы хотите использовать их, вы должны вынуть их из локального пространства процесса и поместить в глобальное пространство.

Итак, чтобы проверить fifo_no_write_when_full * вы могли бы:

  • перенести декларацию count в глобальном пространстве
  • добавить ярлык fifo_write: Вот:
                :: command == PUSH ->
                    if
                        :: count >= FIFO_SIZE ->
                            outputs!IS_FULL(true, src_uid);
                        :: else ->
    fifo_write:
                            data[(head + count) % FIFO_SIZE] = tmp;
                            count = count + 1;
                            outputs!PUSH(data[(head + count - 1) % FIFO_SIZE], src_uid);
                    fi
  • проверить свойство:
    ltl fifo_no_write_when_full { [] ( (count >= FIFO_SIZE) -> ! fifo@fifo_write) }

В-третьих, перед любой попыткой проверить любое из ваших свойств с помощью обычных команд, например

~$ spin -a fifo.pml
~$ gcc -o fifo pan.c
~$ ./fifo -a -N fifo_no_write_when_full

вы должны изменить producer а также consumer так что ни один из них не выполняется в течение неопределенного периода времени и, следовательно, удерживает пространство поиска на небольшой глубине. В противном случае вы можете получить ошибку такого рода

error: max search depth too small

и убедитесь, что проверка исчерпала все ваши аппаратные ресурсы, не придя к разумному выводу.


*: собственно имя fifo_no_write_when_full является достаточно общим и может иметь несколько интерпретаций, например

  • fifo не выполняет push когда он полон
  • producer не может push если fifo является full

В приведенном мною примере я выбрал первую интерпретацию свойства.

Другие вопросы по тегам