Skip to content

Repository files navigation

csa-lab4

Laboratory work № 4. Experiment

Амузинский Артем Андреевич, P3206

  • Вариант: forth | stack | neum | mc | tick | binary | stream | port | pstr | prob1 | cache
  • Расшифровка варианта:
    • forth: forth-like stack-based syntax with Reverse Polish Notation (RPN)
    • stack: stack-based CPU architecture
    • neum: Von Neumann architecture
    • mc: microcoded control unit
    • tick: cycle-accurate simulation
    • binary: true binary machine code
    • stream: stream based I/O
    • port: port-mapped I/O
    • pstr: Length-prefixed (Pascal string)
    • prob1: Find the largest palindrome made from the product of two 3-digit numbers.

Недочеты первой попытки сдачи (31.05.2025)

Почему-то удалился комментарий из гугл таблицы

img.png

Семантика

  • Стратегия вычислений - стековая.
  • Постфиксная запись (обратная польская нотация).
  • Стек возврата — хранит адреса возврата для работы процедур и иногда хранит промежуточное значение, например для команды swap, но после выполнения команды возвращается в изначально значение.
  • Сначала код полностью транслируется, потом последовательно выполняется.
  • Область видимости: все переменные и все функции доступны везде.
  • Термом S" <последовательность_символов>" объявляются строки, любое число определяется как знаковое. Строки, записанные не в указанном формате, трактуются как названия переменных или функций. Число может быть записано в десятичном или шестнадцатиричном формате.
  • Код всегда будет начинать выполнение с метки _start:
<program> ::= <line>*

<line> ::= <label> <comment>? "\n"
        | <instr> <comment>? "\n"
        | <comment> "\n"

<label> ::= <label_name> ":"

<instr> ::= <op0>
         | <op1> " " <label_name>
         | <op1> " " <integer>
         | <op1> " " <variable>

<op0> ::= "drop"
       | "dup"
       | "swap"
       | "+"
       | "-"
       | "*"
       | "/"
       | "mod"
       | "negate"
       | "="
       | "<"
       | ">"
       | "and"
       | "or"
       | "xor"
       | "invert"
       | "if"
       | "!"
       | "@"
       | "halt"
       | "ret"
       | "c"

<op1> ::= "lit"
       | "call"
       | "jump"
       | "in"
       | "out"

<positive_integer> ::= [0-9]+

<lowercase_letter> ::= [a-z]
<uppercase_letter> ::= [A-Z]
<letter> ::= <lowercase_letter> | <uppercase_letter>
<digit> ::= [0-9]
<integer> ::= "-"? <digit>+
string ::= S" <любые символы>"
<letter_or_digit> ::= <letter> | <digit>
<letter_or_digit_or_underscore> ::= <letter_or_digit> | "_"

<label_name> ::= <letter> <letter_or_digit_or_underscore>*

<comment> ::= " "* ";" " "* <comment_text>?
<comment_text> ::= <letter_or_digit_or_underscore> ( " " <letter_or_digit_or_underscore> )*

Организация памяти

Организация памяти:

  • Вся внешняя память - статическая, SRAM
  • Адресация – прямая абсолютная. Загрузка значения на вершину стека (LIT).
  • Программа и данные хранятся в общей памяти согласно архитектуре Фон-Неймановского процессора. Программа состоит из набора инструкций, последняя инструкция – HALT. Процедуры размещаются в той же памяти, они обязаны завершаться при помощи инструкции RET.
  • Операция записи в память перезапишет ячейку памяти как ячейку с данными. Программист имеет доступ на чтение/запись в любую ячейку памяти.

Организация стека:

  • Стек реализован в виде отдельной структуры память, представляющего собой массив, у которого можно использовать только первое и второе значение.
  • Стек 32-разрядный и позволяет полностью помещать один операнд в одну ячейку памяти.

Система команд

Особенности процессора:

  • Доступ к памяти осуществляется по адресу из специального регистра. Значение в нем может быть защелкнуто либо из PC, либо из вершины стека
  • Обработка данных осуществляется в стеке. Данные попадают в стек из памяти, либо из устройств ввода/вывода.
  • Поток управления:
    • Значение PC инкрементируется после исполнения каждой инструкции,
    • Условные (if) и безусловные (jump) переходы.

Набор инструкций:

drop – Удаляет верхний элемент стека.
dup – Дублирует верхний элемент стека.
swap – Меняет местами два верхних элемента стека.
+ – Складывает два верхних элемента стека.
- – Вычитает верхний элемент из второго.
* – Умножает два верхних элемента.
/ – Делит второй элемент на верхний.
mod – Остаток от деления второго элемента на верхний.
negate – Инвертирует знак верхнего элемента.
= – Проверяет равенство двух верхних элементов, кладет 1 (истина) или 0 (ложь).
< – Проверяет, меньше ли второй элемент, чем верхний.
> – Проверяет, больше ли второй элемент, чем верхний.
and – Побитовая операция И для двух верхних элементов.
or – Побитовая операция ИЛИ для двух верхних элементов.
xor – Побитовая операция Исключающее ИЛИ.
invert – Инвертирует все биты верхнего элемента.
if {address} – Условное выполнение: если верхний элемент стека не 0, выполняет код после if.
! – Записывает значение во вторую ячейку памяти (store).
@ – Читает значение из памяти (addr @).
in {port} – Считывает один символ с клавиатуры и кладет его код в стек.
lit <literal> – Загрузка литерала в вершину стека.
out {port} – Загрузка вершины стека в буфер вывода.
jump {address} – Переход на метку.
call {address} – Вызов процедуры.
ret – Возвращение из процедуры.
c – Загрузить значение Carry-flag в стек.
halt – остановка тактового генератора.

Транслятор

запуск:

translator.py input output.bin
input - входной файл с ассемблерным кодом
output.bin - бинарный файл, в который будет транслироваться код, так же
будет сделан output.bin.hex с мнемониками и 16-ричной трансляцией кода

Трансляция реализуется в два прохода:

  1. Первый проход — генерация машинного кода без подстановки переходов и расчёт меток
    • 1.1 Парсинг исходного текста в термы (text_to_terms)

      • Исходный текст разбивается на строки, каждая строка — на слова.
    • Каждое слово анализируется:

      • Если это метка (оканчивается на :), она сохраняется отдельно.
      • Если это известный символ (операция, литерал, имя переменной), он сохраняется как Term(line, pos, symbol).
    • 1.2 Формирование таблицы меток (labels)

      • Итерация 1: считаем количество инструкций до каждой метки (метки не входят в подсчёт).
      • Итерация 2: присваиваем каждой метке её смещённый адрес.
    • В new_labels производится корректировка адресов с учётом инструкций с аргументами (lit, jump, call, if, in, out), которые занимают дополнительное место в памяти (4 байта вместо 1–2).

  2. Второй проход — генерация команд с подстановкой адресов переходов
    • 2.1 Фильтрация термов
      • Метки удаляются из списка термов: они уже обработаны и присутствуют в labels.
    • 2.2 Генерация инструкций
      • По каждому Term строится инструкция:
        • if, jump, call: преобразуются в соответствующий Opcode, аргументом становится адрес из new_labels.
        • lit: за ним ожидается число (десятичное или шестнадцатеричное 0x...), сохраняется как аргумент.
        • in, out: за ними указывается номер порта.
      • Остальные команды: трансляция без аргумента, ищется соответствующий Opcode в symbol_to_opcode.
  3. Преобразование в машинный код
    • to_bytes Каждая инструкция переводится в байты:
      • 1 байт на opcode;
      • 3 байта на аргумент (если он есть). Команда с аргументом:

    ┌─────────┬─────────────────────────────────────────────────────────────┐
    │ 31...24 │ 23                                                        0 │
    ├─────────┼─────────────────────────────────────────────────────────────┤
    │  опкод  │                      аргумент                               │
    └─────────┴─────────────────────────────────────────────────────────────┘

Команда без аргумента:


    ┌─────────┐
    │  7...0  │ 
    ├─────────|
    │  опкод  │ 
    └─────────┘
  1. Сохранение результатов
    • Исходный код читается из файла.
    • Переводится в список инструкций.
    • Генерируются два представления:
      • бинарное (.bin);
      • шестнадцатеричное (.hex).
    • Все сохраняется в указанный путь

Модель процессора

запуск:

machine.py output.bin input_stream [--quiet <without prints>]
output.bin - входной файл с уже транслированным бинарным кодом
input_stream - поток ввода
--quiet - флаг для отключения вывода процессора

Описание реализации:

  • Микропрограммное управление.
  • Функция process_next_command переводит Opcode в микрокод и исполняет его потактово (каждый такт выполняется сигнал).
  • Процесс моделирования – потактовый.
  • Начало симуляции происходит в функции simulation. Процесс моделирования продолжается до исполнения инструкции HALT.
  • Переполнения стека в данной модели не предусмотрено.
  • Особенность реализации: реализован mpc, который позволяет избежать множественного дублирования микрокоманд, например такое есть во всех операциях АЛУ. Микрокод на команду будет выполняться, пока меняется mpc.

Описание микрокода: Каждая микропрограмма содержит 41 сигнала

1. sf - открыть защелку с первого значения стека
2. ss - открыть защелку со второго значения стека
3. sm - сдвинуть стек
4. sp - запушить на стек
5. alul - открыть левый вход алу
6. alur - открыть правый вход алу
7. c - выставить carry флаг
8. z - выставить zero флаг
9. ta - из стека в регистр а
10. fa - из регистра а в стек
11. rspu - запушить в стек возврата
12. rspo - запопить из стека возварата
13. ioi - ввод с контроллера ввода-вывода
14. ioo - вывод с контроллера ввода-вывода
15 - 28. - арифметические и логические операции в алу (+ - * ... and or ...)
29 - 30. muxPC - мультиплексор у PC (00 - ничего, 01 - с верхенго входа, 10 - с нижнего)
31 - 34. muxALUS - мультиплексор у алу и стека (000 - tos, 001 - fromRS, 010 - fromA, 011 - fromIOC, 100 - fromALU, 101 - fromMEM, 110 - ничего)
35 - 37. pcinc - увеличение PC (000 - увеличиваем на 1, 001 - увеличиваем на 3, 010 - берем адрес из дата стека, 011 - берем адрес из ретурн стека, 100 - не изменяем)
38. mpcinc - увеличение mpc (0 - +1, 1 - из LUT)
39. mux2CU - защелкнуть из мукса в control unit
40. mux2stack - защелкнуть из мукса в stack
41. mux2alu - защелкнуть из мукса в alu

Рассмотрим как работает команда ADD Она работает в 3 такта, за них выполняются 3 микрокоманды:

  • micro_add (1 1 0 0 1 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 00 000 100 0 0 0 0 0)

    берем первое и второе значение стека, открываем оба входа алу, выполняем сложение и оставляем результат в алу, увеличиваем mpc, и выставляем флаги

  • micro_alu_2_stack (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 100 100 0 0 1 0 0)

    сохраняем значение из алу в стек, увеличиваем mpc

  • micro_next_command (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 110 000 0 0 0 0 1)

    берем следующую микрокоманду, увеличиваем PC

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

Схемы DataPath и ControlUnit

Control Unit:

img.png

Data Path:

img.png

Пример программы Hello world на моем языке

S" Hello, world!" VARIABLE hello_world  ; переменная со строкой
0x0 VARIABLE index                      ; переменная с индексом строки
0x0 VARIABLE len                        ; переменная с длиной строки (тк паскаль строки)
_start:
    lit len
    lit hello_world @
    !                                   ; сохраняем в переменную len длину паскаль строки
    lit index
    lit hello_world
    lit 4
    +
    !                                   ; сохраняем индекс первой буквы
while:
    lit index @ @                       ; загружаем первую букву и выводим ее
    out 2
    lit index
    dup
    @
    lit 4
    +                                   ; переходим на индекс следующей буквы
    !
    lit 1
    lit len @                           ; уменьшаем и сохраняем счетчик длины
    -
    dup
    if end                              ; проверяем счетчик длины на 0, если да, то переходим на метку end, если нет, то продолжаем выводить буквы
    lit len
    swap
    !
    jump while                          ; продолжаем цикл
end:                                    ; завершаем работу программы
    halt

output.bin.hex

0x8 -   1500008C - lit (0000008C)
0xc -   15000050 - lit (00000050)
0x10 -         12 - @
0x11 -         11 - !
0x12 -   15000088 - lit (00000088)
0x16 -   15000050 - lit (00000050)
0x1a -   15000004 - lit (00000004)
0x1e -          3 - +
0x1f -         11 - !
0x20 -   15000088 - lit (00000088)
0x24 -         12 - @
0x25 -         12 - @
0x26 -   16000002 - out (00000002)
0x2a -   15000088 - lit (00000088)
0x2e -          1 - dup
0x2f -         12 - @
0x30 -   15000004 - lit (00000004)
0x34 -          3 - +
0x35 -         11 - !
0x36 -   15000001 - lit (00000001)
0x3a -   1500008C - lit (0000008C)
0x3e -         12 - @
0x3f -          4 - -
0x40 -          1 - dup
0x41 -   1000004F - if (0000004F)
0x45 -   1500008C - lit (0000008C)
0x49 -          2 - swap
0x4a -         11 - !
0x4b -   17000020 - jump (00000020)
0x4f -         14 - halt
0x50 -          D - hello_world
0x54 -         48 - hello_world
0x58 -         65 - hello_world
0x5c -         6C - hello_world
0x60 -         6C - hello_world
0x64 -         6F - hello_world
0x68 -         2C - hello_world
0x6c -         20 - hello_world
0x70 -         77 - hello_world
0x74 -         6F - hello_world
0x78 -         72 - hello_world
0x7c -         6C - hello_world
0x80 -         64 - hello_world
0x84 -         21 - hello_world
0x88 -          0 - index
0x8c -          0 - len

Запуск golden-тестов

(.venv) ~\PycharmProjects\csa-lab4 git:[main]
pytest 
C:\Users\artem\PycharmProjects\csa-lab4\.venv\Lib\site-packages\pytest_golden\plugin.py:53: GoldenTestUsageWarning: Add 'enable_assertion_pass_hook=true' to pytest.ini for safer usage of pytest-golden.
  warnings.warn(
==================================================================================================== test session starts ====================================================================================================
platform win32 -- Python 3.11.8, pytest-8.3.5, pluggy-1.6.0
rootdir: C:\Users\artem\PycharmProjects\csa-lab4
configfile: pytest.ini
plugins: anyio-4.9.0, golden-0.2.2
collected 7 items                                                                                                                                                                                                            

golden_test.py .......                                                                                                                                                                                                 [100%]

===================================================================================================== 7 passed in 1.24s =====================================================================================================```

работа тестов на github:

Run poetry run coverage run -m pytest
/home/runner/.cache/pypoetry/virtualenvs/forth-yJKRdTo1-py3.11/lib/python3.11/site-packages/pytest_golden/plugin.py:53: GoldenTestUsageWarning: Add 'enable_assertion_pass_hook=true' to pytest.ini for safer usage of pytest-golden.
  warnings.warn(
============================= test session starts ==============================
platform linux -- Python 3.11.12, pytest-7.4.4, pluggy-1.6.0
rootdir: /home/runner/work/csa-lab4/csa-lab4
configfile: pytest.ini
plugins: golden-0.2.2
collected 7 items

golden_test.py .......                                                   [100%]

============================== 7 passed in 2.65s ===============================
Run poetry run coverage report
Name             Stmts   Miss  Cover
------------------------------------
golden_test.py      53      0   100%
isa.py             113      8    93%
machine.py         396     61    85%
translator.py      118     13    89%
------------------------------------
TOTAL              680     82    88%

Для тестирования использовал все необходимые программы, а именно:

  1. программа вывода Hello World!
  2. программа hello_user - golden/hello_user_name0.yml (для вопроса про имя), golden/hello_user_name1.yml (для приветствия пользователя)
  3. программа cat для вывода пользовательского ввода - golden/cat.yml
  4. программа arifm, которая отвечает за переполнение 32-битного регистра алу и сохранения этого числа в памяти - golden/carry_check.yml
  5. программа sort - сортировка массива, который ввел пользователь - golden/sort.yml
  6. программа prob1 для решения проблемы Эйлера - golden/prob.yml

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages