Variant: asm | risc | harv | hw | instr | struct | trap | port | cstr | prob1
Базовый вариант
program ::= { line }
line ::= section_declaration "\n"
| [ label ] instr [ comment ] "\n"
| [ label ] data_command [ comment ] "\n"
| label [ comment ] "\n"
| [ comment ] "\n"
section_declaration ::= "section" section_type
section_typ ::= ".data"
| ".code"
label ::= label_name ":"
comment ::= ";" <any symbols except "\n">
instr ::= instr_arg0
| instr_arg1
| instr_arg2
| instr_arg3
instr_arg0 ::= "di"
| "ei"
| "ret"
| "nop"
| "halt"
instr_arg1 ::= "jmp" label_name
| "be" label_name
| "bne" label_name
| "bg" label_name
| "ble" label_name
| "inc" reg_name
| "call" label_name
| "push" reg_name
| "pop" reg_name
instr_arg2 ::= "mov" reg_name, reg_name
| "movn" reg_name, int
| "in" reg_name, port_id
| "out" port_id, reg_name
| "la" reg_name, label_name
| "lw" reg_name, label_name
| "lb" reg_name, label_name
| "lwi" reg_name, reg_name
| "lbi" reg_name, reg_name
| "stw" label_name, reg_name
| "stb" label_name, reg_name
| "stwi" reg_name, reg_name
| "stbi" reg_name, reg_name
| "cmp" reg_name, reg_name
| "test" reg_name, reg_name
instr_arg3 ::= "add" reg_name, reg_name, reg_name
| "sub" reg_name, reg_name, reg_name
| "mul" reg_name, reg_name, reg_name
| "rem" reg_name, reg_name, reg_name
| "and" reg_name, reg_name, reg_name
data_command ::= "byte" int
| "char" char
| "str" string
| "vec" label_name
int ::= [ "-" ] { <any of "0-9"> }
char ::= <any symbols except "\n">
string ::= " {<any symbols except "\n">} "
port_id ::= 0 | 1
reg_name ::= reg0
| reg1
| reg2
| reg3
| reg4
| reg5
| reg6
| reg7
label_name ::= <any of "a-z A-Z _"> { <any of "a-z A-Z 0-9 _"> }
Метки - алиасы для адресов памяти.
label: ; - определение метки
jmp label ; - использование меток в коде
На этапе трансляции метки заменяются численными адресами
Память организвана согласно Гарвардской модели, где память инструкций и память данных разделены
-
Память инструкций представлена в виде высокоуровневой стрктуры данных, размер машинног слова не оперделен
-
Память данных реализуется массивом байт, размер машинного слова - 4 байта
-
Программисту доступна память данных для записи и чтения, а также регистры общего назначения (reg1 - reg6)
-
Прерывания устанавливаются програмистом при написании программы. В памяти данных храмятся адреса подпрограм обработки прервыаний, так, что 4 * n - адрес обработки прерывания n-ого порта
lw reg, addr
- Загрузить значение размером в 1 машинное слово из ячейки памяти с адресом addr в регистр reg
lb reg, addr
- Загрузить значение размером в 1 байт из ячейки памяти с адресом addr в регистр reg
lbi regt, regs
- Загрузить значение размером в 1 байт из ячейки памяти с адресом указанным в regs в регистр regt
stb addr, reg
- Загрузить младший байт из регистра reg в память по адресу addr
stw addr, reg
- Загрузить значение размером в 1 машинное слово из регистра reg в память по адресу addr
stbi regt, regs
- Загрузить младший байт из регистра regs в память по адресу указанному в регистре regt
stwi regt, regs
- Загрузить значение размером в 1 машинное слово из регистра regs в память по адресу указанному в регистре regt
cmp reg1, reg2
- Вычесть значение регистра reg2 из reg1 по получившемуся результаты выставить флаги состояния
test reg1, reg2
- Выполнить операцию побитового или между значениями reg1 и reg2 по получившемуся результаты выставить флаги состояния
jmp lbl
- Безусловный переход по адресу метки lbl
be lbl
- Переход по адресу метки lbl, если флаг 'Z' равен 1 (branch if equals)
bne lbl
- Переход по адресу метки lbl, если флаг 'Z' равен 0 (branch if not equals)
bg lbl
- Переход по адресу метки lbl, если флаг 'Z' равен 0 и флаг 'N' равен 1 (branch if greater)
ble lbl
- Переход по адресу метки lbl, если флаг 'Z' равен 1 или флаг 'N' равен 0 (branch if lover or equals)
add regt, regs1, regs2
- Сложить значения регистров regs1 и regs2 и положить результат в regt
sub regt, regs1, regs2
- Вычесть значение регистра regs2 из regs1 и положить результат в regt
mul regt, regs1, regs2
- Перемножить значения регистров regs1 и regs2 и положить результат в regt
rem regt, regs1, regs2
- Сложить значения регистров regs1 и regs2 и положить результат в regt
and regt, regs1, regs2
- Сложить значения регистров regs1 и regs2 и положить результат в regt
inc reg
- Прибавить 1 к значению регистра reg и положить результат в reg
call lbl
- Вызвать подпрограмму по адресу метки lbl. Положив на стек адрес овзврата
ret
- Вернуться из подпрограммы. Перейдя по адресу из стека
push reg
- Положить значение регистра reg на вершину стека.
pop reg
- Загрузить значение из вершины стека в регистр reg
in reg, port
- Записать в reg значение из шины ввода-вывода соответствующее внешнему устройству, подключенному к порту port
out port, reg
- Передать значение регистра reg через шину ввода-вывод устройству подключенному к порту port
di
- Запретить процессору обрабатывать перывания внешних от устройств
ei
- Разрешить процессору обрабатывать перывания внешних от устройств
mov regt, regs
- Записать значение регистра regs в regt
movn reg, n
- Записать значение n в регистр reg
movn reg, lbl
- Записать адрес метки lbl в регистр reg
nop
- Не делать ничего (пустая операция)
halt
- Остановить процессор
Аппаратная часть поддершивает лишь операции с беззнаковыми 32-битными числами. Однако программисту может интерпретировать эти данные удобным ему способом, реализовавая иные типы данных, такие как символы, строки и числа меньших или больших размеров, програмным путем
Инструкции ввода вывода поддерживают только пятмую адресацию, причем адрес чтения/записи может быть записан как в виде адреса в самой инструкции, так и хранится в регистре
lb reg1, 10 ; 1 вариант
movn reg2, 10 ; 2 вариант
lbi reg1, reg2
; результат выполнения обоих вариантов одинаковый
Представлены в высокоуровневом формате JSON
{
<operation> : [
[<arg1>,]
[<arg2>,]
...
]
}
Реализация представлена в translator
cargo run -- <source> [<target>]
Команда запуска симулции, где
- source - има файла на языке программирования
- target (опционально) - имя файла куда будет загружен скомпилированный машинный код
Процесс трансляции разделен на 2 этапа:
- parsing
- Разбиение исходного кода на токены
- удаление коментариев
- Парсинг команд и их аргументов
- Обозначение меток
- linking
- Сопосталение меток реальным адресам
- Превращение команд данных в набор байтов
Реализация представлена в processor
cargo run -- <source> <schedule> [<log>]
Команда запуска симулции, где
- source - имя файла, содержащего результат трансляции программы (машинный код)
- schedule - имя файла в формате json, содержащий расписание ввода в процессор
- log (опционально) - имя файля для вывода журнала работы процессора.
На схеме пердставлена модель control unit-а, а также интерфайса ввода вывода
Схема включает в себя decoder, datapath, память инструкций, указатель инструкций, модуль вычисления адреса вектора прерываний, интерфейс ввода-вывода с портами
Реализация CU предоставлена на языке Rust в файле cpu.rs и дирректории cpu
Симуляция исполнения инструкций реализована в decoder.rs
Реализация интерфейса ввода вывода представлена в io
На уровне CU реализовано несколько управляющих сигналов:
- IPIncDP - сигнал записи инкрементированного значения урказателя инструкций в datapath
- IODP - сигнал записи значения из шины ввода-вывода в datapath
- DPIO - сигнал записи значения из datapath на шину ввода-вывода
Также представлены сигналы выбора шиный ввода мультиплексора для указателя команд и шина выбора текущего порта системы ввода-вывода
На схеме предоставлена модель datapath
Схема включает в себя регистры общего назначения, указатель стека, регистр адреса, память данных и арифметико логическое устройство.
Все элементы схемы соединены 32 битными шинами. Передача на шину данных и считывание их с нее происходит пучем подачи логических сигналов на защелки/вентили.
В datapath из CU ведут 3 шины:
- шина данных инструкции
- шина адреса вектора прерываний
- шина адреса инструкции после инкрементации
Из datapath в CU ведет шина данных
При сполнении инструкций декодер выставляет логические сигналы на различные вентили, тем самым записывая данный с шин в различные места.
Из каждого регистра (кроме регистра адреса) выходит 2 шины с вентилями для записи в левую и правую шины alu и также к каждому регистру подключена шина для записи в него значения из шины данных.
Также к входным шинал alu подключены внешние шины, которые также управляются вентилями.
Также к памяти данных подключены сигналы контролирующие чтение и запись данных
Итого в datapath представлены следующие сигналы:
- Reg0ALUl, Reg0ALUr, ALUoReg0 - сигнал записи в регистр 0
- Reg1ALUl, Reg1ALUr, ALUoReg1
- ...
- Reg7ALUl, Reg7ALUr, ALUoReg7
- SPALUl, SPALUr, ALUoSP - сигнал записи в указатель стека
- AddrR - сигнал записи в регистр адреса
- WriteB, WriteW - _сигнал записать в память значение из шины данных по адресу в регистре данных (1 байт/маш. слово)
- ReadB, ReadW - _сигнал чтения в шину данных значения из памяти по адресу в регистре данных (1 байт/маш. слово)
- IntVecALUl - сигнал записи значения адреса вектора прерывания в шину левого входа alu
- DecALUl - сигнал записи значения из инструкции в шину левого входа alu
Выполняя операции ALU высталяет флаги в зависимости от полученного результата. Поддерживаются следующие флаги:
- zero flag - результат операции равен нулю
- neg flag - результат операции отрицателен (старший бит равен 1)
Тестирование происходит с использование golden tests в формате yaml файлов. Тесты лежат в папке tests
Тесты реализованы в виде скрипта на Python с использованием библиотеки pytest
Каждый тест содержит в себе:
- Исходный код на языке программирования
- Результат работы транслятора
- Консольный вывод
- Журнал работы процессора
Тесты представлены для следующий програм:
-
Исходный код на языке программирования: hello_username.asm
-
Распиание прерываний: строка "Egor\0"
[
[0, 69],
[1, 103],
[2, 111],
[4, 114],
[10, 0]
]
- Вывод в консоль:
Enter your name
Hello, Egor
- Машинный код: hello_username/code.json
- Лог работы процессора: hello_username/log.txt
CI настроен на выполнение golden test-ов и линтера при каждом push-е в репозиторий
jobs содержит в себе 2 задачи:
- test - запускающю утилиту pytest для запуска golden test-ов
- lint - запускающю cargo check для каждого из и трейтов проекта
| Булко Егор Олегович | hello | 17 | - | 9 | 84 | 139 | (asm | risc | harv | hw | instr | struct | trap | port | cstr | prob1) |
| Булко Егор Олегович | cat | 29 | - | 20 | 101 | 286 | (asm | risc | harv | hw | instr | struct | trap | port | cstr | prob1) |
| Булко Егор Олегович | hello_username | 50 | - | 37 | 268 | 529 | (asm | risc | harv | hw | instr | struct | trap | port | cstr | prob1) |