Выполнил Горинов Даниил Андреевич P33151
alg | risc | harv | hw | instr | binary | stream | mem | pstr | prob5 | pipeline
- Без усложнения
<program> ::= statement | program statement
<statement> ::= control_flow | io | allocation | assign | read | write
<assign> ::= name "=" expr | name "[" digit "]" = expr
<io> ::= "gets" letter | "gets" string | "puts" string | "put" name
<allocation> ::= "val" name "=" value ";" | "val" name;" | "val" name "[" digit "]"
<control_flow> ::= conditional | conditional else
<conditional> ::= while | if
<if> ::= "if(" comp_expr ")" block
<else> ::= "else" block
<while> ::= "while(" comp_expr ")" block
<block> ::= "{" program "}"
<comp_expr> ::= "(" expr comparison_sign expr ")" | "(" name ")" | "(" value ")"
<value> ::= string | digit
<string> ::= "\"[\w\s,.:;!?()\\-]+\""
<expr> ::= "(" expr ")" | digit | letter | string
<string> ::= "\"[\w\s,.:;!?()\\-]+\""
<comparison_sign> ::= "==" | ">=" | ">" | "<" | "<=" | "!="
<letter> ::= "[a-zA-Z]+"
<digit> ::= "[0-9]+"
<op> ::= "*" | "/" | "%" | "+" | "-" | "<<" | ">>" | "&" | "|" | "^"
Разработанный язык похож на что-то вроде JavaScript без явной типизации.
Строки разделяются точкой с запятой. Язык поддерживает объявление и инициализацию переменных, математические операции, простейшие списки, конструкции if, if-else, while, while-else, операции чтения и записи (get, gets, put, puts).
Язык поддерживает области видимости, которые определяются блоками while и if.
Пример:
val r = 1;
val i = 2;
val k;
val a;
while (i <= 20) {
if (r % i) {
k = i;
a = k * i;
while (a <= 20) {
k = i * k;
a = k * i;
}
r = k * r;
}
i = i + 1;
}
Работа с памятью
Модель памяти процессора:
По варианту использутется гарвардская архитектура, поэтому память инструкций и память данных разделена.
- Память данных. Машинное слово -- 32 бита, знаковое. Линейное адресное пространство. Реализуется списком чисел, являющихся пространством памяти.
- Память команд. Машинное слово -- 32 бита. Реализуется списком сисел, описывающих инструкции.
i - number of instructions
Memory
Instruction memory
+-----------------------------+
| 00 : instruct 1 |
| 01 : instruct 2 |
| 02 : instruct 3 |
| ... |
| i : halt |
+-----------------------------+
Data memory
+------------------------------+
| 00 : variable 1 |
| 01 : variable 2 |
| 02 : array variable 1,len=l |
| ... |
| l+2 : variable 3 |
| l+3 : variable 4 |
| ... |
| |
+------------------------------+
| INTPUT |
+------------------------------+
| OUTPUT |
+------------------------------+
- Little-Endian
- В языке отсутствует константы, поэтому здесь не будут описаны
- Статические данные последовательно загружаются в память, динамические же хранятся в стеке. Стек, идет с конца data memory
- Адрес возврата перед вызовом функции кладется на стек
- Булевые значения где true - 1 , а false - 0
- Строковые буфферы также статически выделяются в памяти данных при компиляции, изначально заполнены нулями, при их встрече используется адрес начала буффера.
- Строки сохраняются в памяти данных последовательно по мере обнаружения их в программе транслятором. Один символ - одна ячейка памяти
- Для того чтобы загружать значения непосредственно в DataPath существует функциональный элемент - Immediately Generator.
- Литералы не хранятся в статистический памяти
- Как будут размещены литералы, сохранённые в статическую память, друг относительно друга? - Последовательно друг за другом, выравнивания нет.
- Как будет размещаться в память литерал, требующий для хранения несколько машинных слов? - Такими литералами являются строки. Они хранятся в памяти байт за байтом.
- В каких случаях переменная будет отображена на регистр или на статическую память? - Все переменные отображаются в статически аллоцируемую область памяти на этапе компиляции/трансляции.
Особенности процессора:
- Машинное слово - 32 бита
- 4 регистра
- размер команд и типы аргументов фиксированы, имеет 4 типа
- Register
- Immediate
- Branch
- Jump
- каждая инструкция выполняется за 5 этапов (по такту на каждый)
fetch_instruction
- загрузка инструкции из памяти данныхdecode_instruction
- декодирование инструкцийexecute
- выполнение инструкций (вычисления в АЛУ, вычисления флагов по результату сравнения в branch comparator)memory_access
- доступ к памяти - для инструкцийwrite_back
- запись результирующего значения (из памяти или АЛУ в регистр). На этом же этапе в инструкциях переходов переписывается значение pc'a
Инструкция | Номер | операнды | Тип | Пояснение |
---|---|---|---|---|
HALT | 0 | 0 | Instruction | Останов |
LW | 1 | 2 (reg, reg) | Register | dmem [rs2] -> rd |
SW | 2 | 2 (reg, reg) | Register | rs1 -> dmem [rs2] |
LWI | 3 | 2 (reg, imm) | Immediate | dmem [imm] -> rd |
SWI | 4 | 2 (reg, imm) | Immediate | rs1 -> dmem [imm] |
JMP | 5 | 1 (imm) | Jump | PC - imm -> PC |
BEQ | 6 | 3 (rs1, rs2, imm) | Branch | rs1 == rs2 ? imm -> pc |
BNE | 7 | 3 (rs1, rs2, imm) | Branch | rs1 != rs2 ? imm -> pc |
BLT | 8 | 3 (rs1, rs2, imm) | Branch | rs1 < rs2 ? imm -> pc |
BGT | 9 | 3 (rs1, rs2, imm) | Branch | rs1 > rs2 ? imm -> pc |
BNL | 10 | 3 (rs1, rs2, imm) | Branch | rs1 >= rs2 ? imm -> pc |
BNG | 11 | 3 (rs1, rs2, imm) | Branch | rs1 <= rs2 ? imm -> pc |
SEQ | 12 | 3 (reg, reg, reg) | Register | rs1 == rs2 -> rd |
SNE | 13 | 3 (reg, reg, reg) | Register | rs1 != rs2 -> rd |
SGT | 14 | 3 (reg, reg, reg) | Register | rs1 > rs2 -> rd |
SLT | 15 | 3 (reg, reg, reg) | Register | rs1 < rs2 -> rd |
SNL | 16 | 3 (reg, reg, reg) | Register | rs1 >= rs2 -> rd |
SNG | 17 | 3 (reg, reg, reg) | Register | rs1 <= rs2 -> rd |
AND | 18 | 3 (reg, reg, reg) | Register | rs1 & rs2 -> rd |
OR | 19 | 3 (reg, reg, reg) | Register | rs1 | rs2 -> rd |
ADD | 20 | 3 (reg, reg, reg) | Register | rs1 + rs2 -> rd |
SUB | 21 | 3 (reg, reg, reg) | Register | rs1 - rs2 -> rd |
MUL | 22 | 3 (reg, reg, reg) | Register | rs1 * rs2 -> rd |
DIV | 23 | 3 (reg, reg, reg) | Register | rs1 / rs2 -> rd |
REM | 24 | 3 (reg, reg, reg) | Register | rs1 % rs2 -> rd |
ADDI | 25 | 3 (reg, reg, imm) | Immediate | rs1 + imm -> rd |
MULI | 26 | 3 (reg, reg, imm) | Immediate | rs1 + imm -> rd |
SUBI | 27 | 3 (reg, reg, imm) | Immediate | rs1 + imm -> rd |
DIVI | 28 | 3 (reg, reg, imm) | Immediate | rs1 + imm -> rd |
REMI | 29 | 3 (reg, reg, imm) | Immediate | rs1 + imm -> rd |
Процессор в модели содержит 4 регистра общего назначения
Для того чтобы загружать значения непосредственно в DataPath существует функциональный элемент - Immediately Generator.
Инструкции представляют собой 32-битные машинные слова в следующем формате
rd
- register destination - регистр, куда будет записано значение после выполнения инструкцииrs1
иrs2
- register source 1,2 - регистры, значения которых будут использоваться для вычисления результата операцииimm
* - immediate - непосредственное значениеopcode
- номер инструкции
31 30 29 28 27 26 25 5 4 0 Bits
+-----------------------------------------------------------+
| rd | rs1 | rs2 | | opcode | Register type
+-----------------------------------------------------------+
| rd | rs1 | imm[22:21] | imm[20:0] | opcode | Immediate type
+-----------------------------------------------------------+
| imm[22:21] | rs1 | rs2 | imm[20:0] | opcode | Branch type
+-----------------------------------------------------------+
| imm | opcode | Jump type
+-----------------------------------------------------------+
* imm - имеет переменный размер, соответствующая типу инструкции часть извлекается из инструкции в immediate generator
Программы так же представлены в виде бинарного представления
- Машинный код сериализуется в бинарный код.
- Сначала в бинарный код записываем всю выделенную data memory
- Затем в бинарный код переводится список словарей с ключами opcode и args. Пример словаря:
{'opcode': 'LW', 'args': [1, 4, 0]}
Интерфейс командной строки: translator.py <input_file> <target_json_file> <target_bin_file>
Реализовано в модуле translator.py
Этапы трансляции (функция translate):
- preprocess - Предобработка. Удаление пустых строк и комментариев, а также построчное разбиение
- tokenize - Токенизация. Трансформирование текста в deque токенов.
- buildAST - Перевод представления, полученного после токенизации, в дерево AST.
- translate - Трансляция АСТ в машинный код.
Реализовано в модуле machine.py
Реализован в классе ControlUnit
.
- Hardwired (реализовано полностью на python).
- Моделирование на уровне инструкций.
- Трансляция инструкции в последовательность (5 тактов) сигналов:
tick_by_tick
.
Особенности работы модели:
- Для журнала состояний процессора используется стандартный модуль logging.
- Количество инструкций для моделирования ограничено hardcoded константой.
- Остановка моделирования осуществляется при помощи исключений:
EOFError
-- если нет данных для чтения из порта ввода-вывода;StopIteration
-- если выполнена инструкцияhalt
.
- Управление симуляцией реализовано в функции
simulate
.
- hello world - выводит
Hello, world!
в stdin. - cat - программа
cat
, повторяем ввод на выводе. - hello_user_name - чтение имени из файла и вывод приветствия
- prob5 - problem 5
Golden тесты реализованы тут: integration_test
CI:
name: CI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install poetry
run: pip install poetry
- name: Set up Python 3.10.6
uses: actions/setup-python@v4
with:
python-version: "3.10.6"
cache: "poetry"
- name: Install project
run: |
poetry install
- name: Lint with ruff
run: |
poetry run python -m ruff translator.py
poetry run python -m ruff machine.py
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install poetry
run: pip install poetry
- name: Set up Python 3.10.6
uses: actions/setup-python@v4
with:
python-version: "3.10.6"
cache: "poetry"
- name: Install project
run: |
poetry install
- name: Run tests
run: |
poetry run pytest --update-goldens
needs: lint
где:
python3-coverage
-- формирование отчёта об уровне покрытия исходного кода.pytest
-- утилита для запуска тестов.pycodestyle
-- утилита для проверки форматирования кода.E501
(длина строк)pylint
-- утилита для проверки качества кода. Некоторые правила отключены в отдельных модулях с целью упрощения кода.- Docker image
python-tools
включает в себя все перечисленные утилиты. Его конфигурация: Dockerfile.
Пример использования и журнал работы процессора на примере prob5
:
python .\translator.py .\programs\prob5.alg .\programs\prob5.json .\programs\prob5.bin
python .\machine.py .\programs\prob5.bin .\programs\input.txt
Текст вывода и ход работы тут