Написание программы под процессор CYBERcobra
Чтобы максимально "прочувствовать" принцип работы созданного вами процессора, вам необходимо написать один из вариантов программ. Вариант выдаётся преподавателем.
Порядок выполнения задания следующий:
-
В первую очередь необходимо ознакомиться с заданием и изучить пример, приведённый в конце задания. Если возникли вопросы по заданию или примеру, свяжитесь с преподавателем. Чем лучше вы понимаете, что от вас ожидают, тем проще будет выполнить задание.
-
Составьте алгоритм работы программы (буквально возьмите листочек, и нарисуйте блок-схему). Прежде чем вы погрузитесь в увлекательное приключение с ноликами и единицами, вам нужно составить четкую карту вашего путешествия.
-
Проверьте вашу блок-схему на данных из примера. Если все сошлось, проверьте вашу блок-схему на других данных. Не забывайте про краевые случаи (отрицательные числа, деление на ноль, переполнения и прочее — для каждого задания они могут быть разными).
-
После того как вы убедились в работоспособности алгоритма на всех возможных данных, наступает время претворить его в виде двоичной программы.
-
Программа описывается в текстовом файле. Для удобства был написан специальный конвертер, который будет принимать на вход текстовый файл с комментариями и двоичным кодом, разделённым пробелами, а на выход выдавать текстовый файл, которым можно будет проинициализировать память инструкций. Подробнее о конвертере смотрите в параграфе cyberconverter. Пример текстового файла, который сможет принять конвертер:
//J B WS ALUop RA1 RA2 const WA 0 0 00 11111111111111111111111 00001 // загрузить константу -1 в регистр 1 0 0 10 00000000000000000000000 00010 // загрузить значение с входа sw_i в регистр 2 0 0 00 00000000000000000000001 00011 // загрузить константу 1 регистр 3 0 0 01 00000 00001 00011 00000000 00001 // сложить регистр 1 с регистром 3 и поместить результат в регистр 1 0 1 00 11110 00001 00010 11111111 00000 // если значение в регистре 1 меньше значения в регистре 2, возврат на 1 инструкцию назад 1 0 00 00000 00001 00000 00000000 00000 // бесконечное повторение этой инструкции с выводом на out_o значения в регистре 1 -
При реализации условных переходов следует иметь в виду пару правил:
-
Блок ветвления (аналог
if/else) состоит из двух наборов инструкций:- инструкции блока
if(если условие выполнилось) - инструкции блока
else(если условие не выполнилось)
При этом сразу за инструкцией ветвления описываются инструкции блока
else(т.к. в случае невыполнения условия перехода,PCперейдёт к следующей инструкции).
Для того, чтобы после выполнения инструкций блокаelseне начались исполняться инструкции блокаif, в конце блока этих инструкций необходимо добавить безусловный переход на инструкцию, следующую за инструкциями блокаif. - инструкции блока
-
Если вы реализуете не ветвление (аналог блока
if/else), а только проверку условия для выполнения какого-то блока инструкций (аналог блокаifбез блокаelse), вы должны помнить, что блокelseвсё равно есть, просто в этом блоке нет инструкций. Однако вы, как и в прошлом правиле, должны добавить безусловный переход на инструкцию, следующую за инструкциями блокаif.
Этого можно избежать, если инвертировать ваше условие. В этом случае, если ваше инвертированное условие выполнится, вы сможете сразу пропустить нужное количество инструкций и начать исполнять инструкцию за пределами вашего блокаif. Если инвертированное условие не выполнится (т.е. выполнится исходное условие),PCперейдёт к следующей инструкции, где и будут находиться ваши инструкции блокаif.
Звучит достаточно запутанно, поэтому давайте рассмотрим пару примеров. Сначала мы запишем нашу идею на языке Си, а затем перенесём её в двоичный код под архитектуру CYBERcobra:if(reg[1]==reg[5]) { reg[2] = 10; reg[3] = 15; } else { reg[2] = 7; goto if_end; // Поскольку в памяти программы блок else будет идти // сразу за инструкцией условного перехода, необходимо // добавить в конце инструкцию безусловного перехода, // чтобы не начать исполнять инструкции блока if. } if_end:Мы хотим проверить на равенство значения в регистровом файле по адресам
1и5. Если это так, записать значения10и15по адресам2и3соответственно. В противном случае, записать значение7по адресу2.
Это можно реализовать следующей двоичной программой://J B WS ALUop RA1 RA2 const WA 0 1 00 11000 00001 00101 00000011 00000 // Если регистры 1 и 5 равны, // перемести PC на 3 инструкции вперёд // (перешагни через две // инструкции блока else) //--------------------------------------- // блок else //--------------------------------------- 0 0 00 00000000000000000000111 00010 // reg[2] = 7 1 0 00 00000 00000 00000 00000011 00000 // goto if_end //--------------------------------------- //--------------------------------------- // блок if //--------------------------------------- 0 0 00 00000000000000000001010 00010 // reg[2] = 10 0 0 00 00000000000000000001111 00011 // reg[3] = 15 //--------------------------------------- 0 0 00 00000000000000000000000 00000 // некая инструкция с меткой if_end // куда будет перемещен PC после // выполнения блока elseРассмотрим второй пример, где нет блока
else:if(reg[1] == reg[5]) { reg[2] = 10; reg[3] = 15; }Как упоминалось ранее, можно реализовать этот условный переход по той же схеме (тогда пример программы на Си примет вид):
if(reg[1] == reg[5]) { reg[2] = 10; reg[3] = 15; } else { goto if_end; } if_end:А можно инвертировать условие:
if(reg[1] != reg[5]) { } else { reg[2] = 10; reg[3] = 15; }В этом случае, нет нужды делать безусловный переход на инструкцию, следующую за инструкциями блока
if, т.к. там нет никаких инструкций.
Такое условие можно реализовать следующей двоичной программой://J B WS ALUop RA1 RA2 const WA 0 1 00 11001 00010 00101 00000011 00000 // Если регистры 2 и 5 НЕ РАВНЫ, // перемести PC на 3 инструкции вперед // (перешагни через две // инструкции блока else) //--------------------------------------- // блок else //--------------------------------------- 0 0 00 00000000000000000001010 00010 // reg[2] = 7 0 0 00 00000000000000000001111 00011 // reg[3] = 15 //---------------------------------------
-
-
В двоичном программировании, реализация циклов лучше всего делается аналогом
do whileв Си (если вы уверены, что первая итерация цикла гарантированно пройдёт условие выхода из цикла). В этом случае, вы сперва описываете тело цикла, а затем через условный переход возвращаетесь обратно к началу тела цикла. Если условие не выполнилось, вы автоматически выйдете из цикла. -
Для того, чтобы в конце выполнения программы было легко увидеть результат выполнения, в конец программы необходимо добавить инструкцию безусловного перехода, поле
constкоторой равно нулю. В этом случае, будет выполнятьсяPC=PC+0что приведёт к повторению этой инструкции снова и снова. При этом в полеRA1необходимо указать адрес регистра, где хранится результат. На временной диаграмме это отобразится так, что в какой-то момент все сигналы процессора "замрут", а на выходеout_oокажется результат, вычисленный вашей программой. -
После того, как вы написали программу, её необходимо проверить. Для этого сперва необходимо преобразовать её к формату, принимаемому памятью инструкций с помощью программы
cyberconverter. При необходимости, заменить данные в файле, инициализирующем память инструкций актуальными данными. -
Если ваша программа использует данные с внешних устройств, нужно выставить проверяемое вами значение в модуле
testbenchна входsw_iв месте подключения модуляCYBERcobra. -
Проверка работы программы осуществляется аналогично проверке модуля
CYBERcobra— вы достаете внутренние сигналы модуля, и смотрите за поведением сигналов:PC,read_dataпамяти инструкций,flagАЛУ, содержимым регистрового файла. Проверяете, что в конце на выходеout_oразмещено корректное значение.
cyberconverter
cyberconverter — это программа, которая преобразует текстовый файл с инструкциями архитектуры CYBERcobra в текстовый файл, который сможет принять память инструкций.
cyberconverter может обрабатывать файлы, содержащие комментарии (начинающиеся с //), пробелы и пустые строки, а также наборы символов 0 и 1. Комментарии, пробелы и пустые строки удаляются, после чего оставшиеся строки из 32 нулей и единиц конвертируются в шестнадцатеричные значения и записываются в выходной файл.
cyberconverter принимает до двух аргументов. Порядок запуска следующий:
-
Вызов справки:
cyberconverter -h cyberconverter --help -
Преобразование программы, записанной в файле
test.txt, с записью результата в файлprogram.mem:cyberconverter test.txt program.mem -
Если не указан второй аргумент, результат будет записан в файл:
<имя_исходного_файла>_converted.<расширение исходного файла>:cyberconverter test.txtРезультат будет записан в файл
test_converted.txt. -
Если программа будет запущена без аргументов, то исходным файлом будет считаться файл
program.mem.
В случае отсутствия исходного файла, наличия неподдерживаемых символов или неверной длины инструкции будет выведено сообщение об ошибке.
Индивидуальные задания
В приведённых ниже заданиях под a будет подразумеваться некоторое число, заданное в программе (например, в программе прописано a=10), под sw_i — вход с внешних устройств. "Вывести в out_o" — означает, что в конце программы необходимо реализовать бесконечный цикл, с указанием в RA1 адреса регистра, хранящего результат (см. пункт 8 параграфа "Написание программы под процессор CYBERcobra").
В случае, если задание используется для написания программы на ассемблере, sw_i будет обозначать ещё одно число, заданное в программе (как и a), а под "Вывести в out_o" — запись результата в регистр x10 (в назначение этого регистра входит возврат результата функции) в конце выполнения программы.
-
Вычислить циклический сдвиг вправо
a >> sw_i.
Пример:a = 0...01011,sw_i = 0...010.
Результат вычислений:out_o = 110...010. -
Вычислить
a - sw_iбез использования операции вычитания.
Пример:sw_i = 0...011,a = 0...0100.
Результат вычислений:out_o = 0...001. -
Вычислить циклический сдвиг влево
a << sw_i.
Пример:a = 10...01011,sw_i = 0...10.
Результат вычислений:out_o = 0...0101110. -
Поменять местами
[7:0]и[15:8]биты числаsw_i. Вывести результат вout_o.
Пример:sw_i = 0...010100000_1110010.
Результат вычислений:out_o = 0...011100101_10100000. -
Вычислить приблизительное значение длины вектора
(a;sw_i). Вычисляется какmax + min/2, гдеmaxиmin— это большее и меньшее из чиселaиsw_iсоответственно.
Пример:a = 0...011,sw_i = 0...0100.
Результат вычислений:out_o = 0...0101.
-
Вычислить
a * sw_iпосредством суммыsw_iзначенийa. Вывести результат вout_o.
Пример:a = 5,sw_i = 4.5 * 4 == 5 + 5 + 5 + 5 = 20.
Результат вычислений:out_o = 0...010100. -
Если
sw_i[1:0] == 00, то вout_oвыводитсяa, еслиsw_i[1:0] == 01, тоb, еслиsw_i[1:0] == 10, тоc, еслиsw_i[1:0] == 11, тоd.
Пример:a = 0...00,b = 0...010,c = 0...011,d = 0...001,sw_i[1:0] = 01.
Результат вычислений:out_o = 0...010. -
Посчитать длину окружности при заданном радиусе
sw_i, считая, чтоpi = 3. Вывести результат вout_o.
Пример:sw_i = 0...010.
Результат вычислений:out_o = 0...01100. -
Если
sw_iявляется степенью двойки, то вывестиout_o = 0...01, в противном случае,out_o = 0...0.
Пример 1:sw_i = 0...0100. Результат вычислений:out_o = 0...01.
Пример 2:sw_i = 0...0110. Результат вычислений:out_o = 0...00. -
Найти количество нулей в двоичном представлении числа
sw_i. Вывести результат вout_o.
Пример:sw_i = 1...10110_0010.
Результат вычислений:out_o = 0...0101. -
Найти наибольший двоичный разряд числа
sw_i, значение которого равно1. Если такого нет, вывести32. Вывести результат вout_o.
Пример:sw_i = 0...0110.
Результат вычислений:out_o = 0...010. -
Сформировать число, состоящее из чётных двоичных разрядов числа
sw_i. Вывести результат вout_o.
Пример:sw_i = 0...011_1011_1000.
Результат вычисленийout_o = 0...01_0100. -
Найти количество единиц в двоичном представлении числа
sw_i(обратите внимание,sw_i– знаковое число). Вывести результат вout_o.
Пример:sw_i = 0...0101_0110.
Результат вычислений:out_o = 0...0100. -
Найти количество двоичных разрядов, в которых различаются числа
sw_iиa. Вывести результат вout_o.
Пример:sw_i = 0...0110,a = 0...01110.
Результат вычислений:out_o = 0...01. -
Вывести в
out_oподряд все единицы входного числаsw_i.
Пример:sw_i = 0...01011011011.
Результат вычислений:out_o=0...01111111. -
Вывести в
out_oзначение выражения:out = (k*a + b) + c, гдеk— этоsw_i[15:12],a— этоsw_i[11:8],b— этоsw_i[7:4],c— этоsw_i[3:0].
Пример:sw_i = 0...0011_0010_0100_0011.
Результат вычислений:out_o = 0...01101.
-
Найти остаток от деления
sw_iнаa. Вывести результат вout_o.
Пример:sw_i = 0...0101,a = 0...010.
Результат вычислений:out_o = 0...01. -
Найти и вывести в
out_oколичество вхожденийa[2:0]вsw_iбез пересечений.
Пример:a[2:0] = 010,sw_i = 0...01101_0101.
Результат вычислений:out_o = 0...01. -
Определить, сколько раз встречается
11в двоичном представленииsw_iбез пересечений. Вывести результат вout_o.
Пример:sw_i = 0...01110.
Результат вычислений:out_o = 0...01. -
Вывести в
out_oрезультат целочисленного деленияa/sw_i.
Пример:sw_i = 0...010,a = 0...0111.
Результат вычислений:out_o = 0...011. -
Вывести в
out_oсуммуsw_i[3:0]+sw_i[7:4]+sw_i[11:8]+sw_i[15:12].
Пример:sw_i[15:0] = 0001_0010_0011_0000.
Результат вычислений:out_o = 0...0110. -
В числе
sw_iзаменить справа-налево каждое00на11. Вывести результат вout_o.
Пример:sw_i = 1...101000.
Результат вычислений:out_o = 1...101011.
-
Поменять местами чётные биты числа
sw_iс нечётными битами этого числа (то есть соседние биты поменять местами). Вывести результат вout_o.
Пример:sw_i = 0...01010_0111.
Результат вычислений:out_o = 0...0101_1011. -
Инвертировать первые
sw_iбит числаa. Вывести результат вout_o.
Пример:sw_i = 0...011,a = 0...01010_0011.
Результат вычислений:out_o = 0...01010_0100. -
Вывести n-ый член последовательности Фибоначчи Fn. n =
sw_i. Вывести результат вout_o.
Пример:sw_i = 0...0100.
Результат вычислений:out_o = 0...010. -
Поменять в числе
aразрядыi = sw_i[4:0]иj = sw_i[9:5]. Вывести результат вout_o.
Пример:a = 0...01001,sw_i[9:0] = 00000_00001. Значит, в числеанеобходимо поменять местамиа[0]иa[1].
Результат вычислений:out_o = 0...01010.
- Вычислить
a * sw_iс использованием операций сложений и сдвига ("в столбик"). Вывести результат вout_o.
Пример:a = 0...01011,sw_i[9:0] = 0...01110.
Результат вычислений:out_o = 0...010011010.
1011 (11 в двоичном виде)
x 1110 (14 в двоичном виде)
======
0000 (это 1011 x 0)
1011 (это 1011 x 1, сдвинутое на 1 влево)
1011 (это 1011 x 1, сдвинутое на 2 влево)
+ 1011 (это 1011 x 1, сдвинутое на 2 влево)
=========
10011010 (154 в двоичном виде)
- Вывести в
out_on-ый член арифметической прогрессии aN, гдеa1 = a,d = sw_i[15:8],n = sw_i[7:0](d и n неотрицательные).
Пример:sw_i[15:8] = 0000_0010,sw_i[7:0] = 0000_0011,a = 0...01.
Результат вычислений:out_o = 0...0101.
- Удалить все вхождения
sw_i[2:0]изaсо сдвигом вправо (заполняя удалённые области).
Пример:a = 0...010011010,sw_i[2:0] = 101.
Результат вычислений:out_o = 0...010010