- Битовые операции. Для чего они нужны на примере программировании МК
- Введение
- Операции битового сдвига:
- О песочнице
- О модерации
- Язык описания аппаратуры Verilog HDL
- Компьютер на логических микросхемах: исполнение инструкций
- Флаги
- Набор инструкций
- Арифметические инструкции
- Загрузка константы
- Загрузка из памяти
- Сохранение в память
- Переходы
- Реализация
- Программирование
- Примечание
- 📸 Видео
Видео:Виды видеопамяти и сколько её нужно? Какая нужна шина?Скачать
Битовые операции. Для чего они нужны на примере программировании МК
Введение
В своё время я не мог понять смысл таких вещей, как битовые операции. Статьи в интернете не давали мне практического понимания этого вопроса. Со временем, когда я столкнулся с программированием микроконтроллеров (МК), я понял удобство подобных операций, поэтому я решил написать данную статью, чтобы пролить свет на этот вопрос людям, которые только услышали про битовые операции и хотят узнать конкретное практическое применение данных операций. Все примеры будут приведены для tms320f28027.
Операции битового сдвига:
Битовые операции, как можно понять из названия, заключаются в сдвиге битов вправо (>>) или влево (
Видео:МОРГЕНШТЕРН УЧИТ ДЕЛАТЬ БИТ ЗА 1 МИНУТУ!!! Как быстро сделать бит The люди MORGENSHTERNСкачать
О песочнице
Это «Песочница» — раздел, в который попадают дебютные посты пользователей, желающих стать полноправными участниками сообщества.
Если у вас есть приглашение, отправьте его автору понравившейся публикации — тогда её смогут прочитать и обсудить все остальные пользователи Хабра.
Чтобы исключить предвзятость при оценке, все публикации анонимны, псевдонимы показываются случайным образом.
Видео:Как выбрать видеокарту. Или почему шина 256 бит - не рулит. (см. описание)Скачать
О модерации
- рекламные и PR-публикации
- вопросы и просьбы (для них есть Хабр Q&A);
- вакансии (используйте Хабр Карьеру)
- статьи, ранее опубликованные на других сайтах;
- статьи без правильно расставленных знаков препинания, со смайликами, с обилием восклицательных знаков, неоправданным выделением слов и предложений и другим неуместным форматированием текста;
- жалобы на компании и предоставляемые услуги;
- низкокачественные переводы;
- куски программного кода без пояснений;
- односложные статьи;
- статьи, слабо относящиеся к или не относящиеся к ней вовсе.
Видео:ЧТО Делать Чтобы Тебя Не Кинул Артист + Шаблон Договора на RU/ENGСкачать
Язык описания аппаратуры Verilog HDL
Казалось бы простая задача: как развернуть биты в шине так, чтоб младший бит стал старшим, а старший самым младшим? Первое, что приходит на ум: написать вот так:
reg [7:0]src;
//reverse?
wire [0:7]re1; assign re1 = src; //does not work..
Но это так не работает!
Какие есть работающие варианты?
Первый и самый понятный способ — это расписывать каждый бит в отдельности. Вот так:
Это отличный метод, который использует оператор конкатенации, объединяет известные сигналы в новом нужном порядке. Этим методом легко пользоваться, когда число сигналов в шине не велико. Если же шина многоразрядная, 64, 128, 1024 бита, то как быть?
Второй метод — это написать и использовать специальную функцию:
function [7:0] fbit_reverse ( input [7:0] data );
integer i;
begin
for ( i=0; i
begin
fbit_reverse[7-i] = data[i];
end
end
endfunction
В эту функцию можно передать восьмибитный сигнал и она выдаст нам другой восьмибитный сигнал, где биты уже развернуты, как нужно. Вот так:
//method 2, use reverse function
wire [7:0]re3; assign re3 = fbit_reverse(src);
Хорошее решение, но не придумал, как сделать его параметризуемым. Что если есть несколько шин которые имеют разную разрядность и каждую нужно развернуть? Тогда можно сделать отдельный параметризуемый модуль. Например, вот так:
module mod_bit_reverse( in, out );
parameter NUM_BITS = 16;
input wire [NUM_BITS-1:0]in;
output wire [NUM_BITS-1:0]out;
genvar i;
generate
for (i=0; i
begin
assign out[NUM_BITS-1-i] = in[i];
end
endgenerate
endmodule
Этот модуль можно вставлять в другие модули и там и сям и задавать разрядность шин в параметре.
Вставить модуль в другой модуль верилом можно так:
wire [7:0]re4;
mod_bit_reverse // module name
#(.NUM_BITS(8)) //module parameter, bus width
my_reverse_module //module instance name
(
.in(src), // module signals
.out(re4)
);
Есть еще один вариант — использовать SystemVerilog Streaming Operator.
Это замечательно работает, если симулировать в Modelsim. Для симуляции подходит даже Modelsim Starter Edition Intel v10.5b, который идет в комплекте с 16м квартусом.
Однако, похоже, что сам квартус такое не поддерживает. Увы.
Симулятор icarus verilog так же на эту строку дает ошибку — что-то вроде «sorry, streaming operators are not supported».
Чтобы не быть голословным, объединяю все описанные выше методы в один Verilog Testbench файл и пробую симулировать в Modelsim.
//source bits which should be reversed
reg [7:0]src;
//this method does not work! no reverse bits!!
wire [0:7]re1; assign re1 = src;
//method 2, use reverse function
wire [7:0]re3; assign re3 = fbit_reverse(src);
//method 3, use reversing module
wire [7:0]re4;
mod_bit_reverse #(.NUM_BITS(8)) my_reverse_module( .in(src), .out(re4) );
//method 4
//SystemVerilog streaming operators do not work neither with icarus verilog nor with Quartus Prime
//Modelsim Starter Edition v10.5b supports this
wire [7:0]re5;
assign re5 = Читайте также: Шины 155 65 r13 в брянске
src = 8’h1C; #1;
$display(«source %X»,src);
$display(«try1 reverse %X»,re1);
$display(«try2 reverse %X»,re2);
$display(«try3 reverse %X»,re3);
$display(«try4 reverse %X»,re4);
$display(«try5 reverse %X»,re5);
#10;
#10;
$finish();
end
function [7:0] fbit_reverse ( input [7:0] data );
integer i;
begin
for ( i=0; i begin
fbit_reverse[7-i] = data[i];
end
end
endfunction
module mod_bit_reverse( in, out );
parameter NUM_BITS = 16;
input wire [NUM_BITS-1:0]in;
output wire [NUM_BITS-1:0]out;
genvar i;
generate
for (i=0; i begin
assign out[NUM_BITS-1-i] = in[i];
end
endgenerate
endmodule
Еще напишу вспомогательный файл TB.DO со списоком команд симулятора Modelsim:
vlib work1
vlog -work work1 tb.v
vsim work1.tb
run -all
Запускаю в командной строке Modelsim:
>vsim -c -do tb.do
В консоли видно, как верилоговская системная функция $display выводит развернутый байт для методов 2, 3, 4 и 5. Метод 1, как я и говорил — не работает.
Кроме того, тестбенч создает файл OUT.VCD, который содержит информацию о сигналах проекта и который можно посмотреть с помощью gtkwave. Вот эти сигналы (скриншот программы gtkwave):
Ну и напоследок, пожалуй нужно заметить, что любая логика разворачивающая шину, даже модуль mod_bit_reverse, в микросхеме FPGA абсолютно не занимает никакого места. И это правильно, ведь фактически, никакой логики там и нет — есть просто переименование сигналов.
Видео:Первый Бит | Маркировка шин: подключись к системе за 5 простых шагов. Честный знакСкачать
Компьютер на логических микросхемах: исполнение инструкций
В голосовании к прошлой статье с небольшим отрывом победила видеокарта, но так как у нас тут не демократия, а конституционная монархия, про видеокарту будет следующая статья, а эта – про кодирование инструкций и их исполнение.
Видео:В ЧЕМ ОТЛИЧИЕ ЛИЗИНГ ОТ ЭКСКЛЮЗИВ В БИТАХ? ЧТО ЛУЧШЕ ПОКУПАТЬ! КАК ГРУЗИТЬ НА ПЛОЩАДКИ ПРАВИЛЬНОСкачать
Флаги
Перед тем, как перейти к самим инструкциям, расскажу о флагах. В процессоре флаги – это однобитовые значения, которые изменяются в результате арифметических действий (или других событий) и используются для условных переходов или других условных операций. В моем процессоре четыре флага:
O – переполнение или знаковый перенос.
При программировании на ассемблере нет знаковых или беззнаковых чисел: любое число можно рассматривать либо так, либо этак. Например, утверждения «регистр A содержит -1» и «регистр A содержит 255» значат одно и то же. Всё дело в интерпретации двоичного числа человеком. Рассмотрим пример: в восьмибитном регистре A находится двоичное значение 1000 0010, а в B – 0111 1101. Посмотрим на результаты сложения и вычитания этих чисел и их знаковых и беззнаковых интерпретаций.
В случае со сложением беззнаковый результат «неправильный»: должно получиться 256, но из-за переполнения байта получается ноль. В случае с вычитанием наоборот: должно получиться -252, но такое число нельзя закодировать в 8 бит, поэтому получается 4. Для обнаружения этих ситуаций и нужны флаги С и O: С устанавливается в случае «неправильных» беззнаковых результатов, а O – в случае «неправильных» знаковых. Подробнее можно почитать тут.
Набор инструкций
В моем процессоре пять классов инструкций: арифметические, загрузка, сохранение, загрузка константы и переходы.
Код любой инструкции имеет размер один байт. Этот байт в начале каждого цикла загружается в регистр текущей инструкции IR. Таким образом процессор «знает», какую инструкцию он сейчас исполняет.
Арифметические инструкции
Старший бит кода арифметической инструкции ноль. Он и определяет этот класс инструкций.
Следующие четыре бита – код арифметической операции, напрямую подаваемый на вход АЛУ . Всего 16 операций:
ADC – сложение с переносом,
SBB – вычитание с переносом,
XOR – побитовое исключающее ИЛИ,
SHL – сдвиг влево на один бит,
SHR – логический сдвиг вправо на один бит,
SAR – арифметический сдвиг вправо на один бит,
EXP – устанавливает регистр в 00 или FF в зависимости от флага переноса.
Дальше идет бит инверсии, определяющий порядок операндов. Как мы помним, из-за жесткой привязки выхода регистра A к первому входу АЛУ один из аргументов должен обязательно быть А. Этот бит и определяет, первый это аргумент (0) или второй (1).
Пояснение про порядок операндов
Если вы не знакомы с ассемблером (диалект Intel), то нужно помнить, что первый операнд инструкции – это то, куда записывается результат. Например:
Тут результат вычитания a из b будет записан в b. На си-подобном языке это было бы так:
И последние два бита – это индекс регистра, используемого в качестве второго операнда.
Комбинация 00 должна соответствовать регистру A, но он не подключен к ведущей на второй вход АЛУ шине, поэтому при использовании этой комбинации на этой шине будет ноль благодаря подтягивающим резисторам. Таким образом возможна арифметика между A (фиксированным первым операндом) и нулем.
Для примера рассмотрим инструкцию ADD PL, A . Битовое представление будет следующим:
бит инверсии установлен ( 1 ), так как А – второй аргумент,
Читайте также: Контроллер универсальной последовательной шины usb asrock
Итого 01001110 или 0x4E . На знакомой блок-схеме исполнение этой инструкции будет выглядеть так:
Красными стрелками обозначены сигналы, которые будут активны при исполнении этой инструкции. Звездочки показывают, какое устройство активно, т.е. определяет уровни сигналов, на конкретной шине. Теперь рассмотрим временную диаграмму исполнения этой инструкции:
Примечание для дотошных
На диаграмме все сигналы для простоты имеют активный уровень 1, хоть реально это может быть и не так, многие сигналы инверсны.
Самый верхний сигнал на диаграмме – тактовый сигнал. Следующий за ним сигнал cycle – это просто тактовый сигнал с частотой в два раза ниже. Он определяет цикл исполнения инструкции: загрузка опкода или исполнение.
По нисходящему фронту тактового сигнала (2) значение с шины данных защелкивается в регистр IR, так как сигнал записи в этот регистр we_ir активен.
Дальше (3) cycle переключается в 1, что означает исполнение инструкции. Одновременно с ним переключаются необходимые для исполнения этой конкретной инструкции сигналы:
we_pl указывает, что значение со внутренней шины должно быть записано в регистр PL;
oe_pl_alu – что выходной буфер PL должен включиться и подать напряжение на шину, ведущую в ALU;
oe_alu – что АЛУ должно активировать свой выходной буфер и подать результат на внутреннюю шину;
we_flags – что в регистр флагов тоже должно быть записано значение.
Сигнал we_ir , наоборот, отключается, чтобы значение в IR сохранилось еще на такт. А благодаря активному сигналу inc_ip по нисходящему фронту clk счетчик инструкции инкрементируется (4).
Вся запись в регистры происходит по нисходящему фронту clk . Таким образом, между восходящим фронтом (3), когда управляющие сигналы переключаются, и нисходящим (4) значения на всех шинах успевают установиться, и в регистры попадают правильные значения.
Когда cycle возвращается в ноль (5), инструкцию можно считать выполненной. На шине данных уже находится опкод следующей инструкции, который будет загружен в IR в момент времени (6).
Итого, арифметическая инструкция занимает два такта.
Инструкции INC, DEC, NOT, NEG, SHL, SHR, SAR, EXP принимают один аргумент, хоть и кодируются так же, как и остальные. Можно считать, что второй аргумент они просто игнорируют.
Еще один момент: MOV – это тоже «арифметическая» инструкция, поэтому она должна менять флаги ( we_flags активен). Это очень неудобно при программировании, поэтому в случае этой конкретной операции we_flags остается неактивным, флаги сохраняются.
Загрузка константы
Здесь первые три бита опкода должны быть 110 , а последние два кодируют регистр. Иксами помечены биты, значение которых неважно. Сразу за опкодом в памяти следует константа, которую надо загрузить. На ассемблере эта инструкция обозначается LDI .
Код инструкции: 11000001 01011010
Здесь вступает в игру новый сигнал – supercycle , который является замедленным в два раза cycle . Благодаря ему исполнение инструкции LDI занимает четыре такта. IP инкрементируется два раза (4 и 8). В середине (6), когда на шине данных константа для загрузки, сигналом oe_d_di активируется буфер, соединяющий внешнюю шину данных со внутренней, и значение с этой шины защелкивается в регистр B благодаря активному we_b .
Активные сигналы в момент времени (6)
Загрузка из памяти
Последние два бита так же, как и в LDI, кодируют регистр, в который будет загружено значение. Как вы помните, адрес памяти, откуда будет взят байт, хранится в PH:PL.
Здесь по уже знакомому сигналу cycle активируется сигнал addr_p (3), по которому на шину адреса выводится значение из PH:PL вместо IP. Как и в LDI, значение с внешней шины данных передается на внутреннюю, а с нее по нисходящему фронту clk (4) защелкивается в нужный регистр. Инструкция исполняется за два такта.
Активные сигналы в момент времени (4)
Сохранение в память
Здесь под код регистра отводится только один бит (A или B), потому что сохранение PL или PH не имеет смысла: в них хранится адрес той ячейки, куда сохраняем!
На этой диаграмме шина D показана иначе, чем раньше: тут показано то значение, которое устанавливает на нее процессор. По умолчанию шина находится в плавающем состоянии, но по сигналу oe_b_d (3-5) на нее подается значение из регистра B. Так как в это же время сигнал oe_mem становится неактивным, конфликта на шине не будет: память ее «отпускает». По нисходящему фронту we_mem (4) значение с шины данных записывается в память по адресу из P, который, как и в случае с LD, устанавливается на шине с помощью сигнала addr_dp . Сигнал we_cycle – сдвинутый на 90º cycle – нужен для удобства получения сигнала we_mem , который занимает половину периода clk .
Читайте также: Фольксваген поло седан 2017 размеры шин
Активные сигналы в момент времени (4)
Эти тайминги работали отлично, пока я не добавил переключение банков и не стал запускать программы из ОЗУ. Тогда возникла проблема: так как сигнал we_mem устанавливается слишком быстро (3), а на шине еще старый адрес, данные в памяти портились. Чтобы это исправить, я напаял на плате модуля управления схему небольшой задержки сигнала на RC-цепочке. Получились такие тайминги:
Теперь адрес успевает установиться до того, как будет запрошена запись.
Схема для задержки сигнала
Здесь сигналы we_mem_orig и we_mem инверсны (активный ноль).
Переходы
Этот код кодирует сразу безусловные переходы, условные переходы и NOP («безусловные непереходы»).
Два бита FF кодируют флаг для проверки (Z, C, S, O).
Бит I определяет инверсию результата проверки.
Бит E определяет, будут ли вообще проверяться флаги. Если он единица, то флаги не проверяются, а результат проверки считается единицей. Поэтому, если выставлены одновременно биты E и I , то переход не произойдет (NOP), а если E выставлен, а I сброшен, то произойдет безусловно (JMP).
Диаграмма выглядит так же, как и остальные, кроме сигнала swap_p , который устанавливается в единицу, если должен быть выполнен переход. Если swap_p выставлен в единицу, по нисходящему фронту clk (4), переключается флаг, определяющий, какой из физических регистров в блоке P – это IP, а какой PH:PL.
Переключение селектора P
Видео:Влияние шин PCI-e и внутренней шины видеокарты на производительностьСкачать
Реализация
Вот и все инструкции. Имея временные диаграммы, несложно нарисовать логические схемы, которые должны выдавать все управляющие сигналы. Эти схемы реализованы на синей плате, а управляющие сигналы наглядно выведены на светодиоды.
Модуль управления
Когда я только начинал делать процессор, я хотел сделать по-другому: чтобы быстрее получить что-то работающее, использовать ПЗУ с таблицей значений вместо дискретной логики. Оказалось, что так не получится: при переключении адресов ПЗУ выдает неопределенные значения, случайные всплески сигналов, поэтому тот вариант оказался совершенно неработоспособным. Процессор вел себя случайным и непредсказуемым образом. С АЛУ, однако, такой подход прошел, потому что значения с выхода АЛУ не используются как управляющие сигналы. Моя первая версия АЛУ была на двух микросхемах ПЗУ, но потом я его тоже переделал.
Программирование
У меня используется классическая (для C или C++) модель сборки. Компилятор выдает ассемблерный код, ассемблер делает из него объектные файлы, линкер собирает их в бинарный файл для прошивки в ПЗУ или для записи на SD-карту. Всё это управляется системой сборки SCons – ее очень просто настроить под себя и использовать нестандартные компиляторы.
Линкер оптимизирующий: умеет выкидывать неиспользуемые секции (аналог —gc-sections в GNU ld) и перемешивать их так, чтобы максимально заполнять пустые места, остающиеся от выравнивания.
Компилятор (и язык) я назвал Natrix (латинское название ужа), но по сути это урезанный Си: без неявных кастов, с ограничениями по вызову функций, без конструкций вроде union и switch . Главная проблема для компилятора – это отсутствие аппаратного стека, которую я решаю так: все локальные переменные в функциях выделяются статически, адрес возврата тоже кладется в статическую (уникальную для функции) переменную. Временные переменные для вычисления выражений тоже статические, но общие для всех функций. Аргументы и возвращаемое значение – тоже статические переменные. Такое обилие статических переменных – не проблема, так как нехватки ОЗУ никогда не наблюдается: намного раньше заканчивается память для кода (ха-ха).
Чтобы всё-таки можно было использовать рекурсию, используется программный стек. При компиляции строится граф вызовов, и если обнаружены циклы, то перед этими рекурсивными вызовами все статические переменные вызывающей функции кладутся на стек, а потом восстанавливаются. Это очень дорого, но рекурсия работает.
Умножение, деление и сдвиг на переменную программные и заменяются на вызовы встроенных функций. Простое умножение (переменной-байта на константу) и сдвиги на константу встраивается.
Пример сгенерированного кода: сдвиг 32-битного числа
Всё это (компилятор, ассемблер и линкер) написано на питоне, потому что я хотел как можно скорее получить работающий результат. И если для ассемблера и линкера это оправдано из-за простоты, а в ассемблере еще и дает преимущество в виде простого вычисления константных выражений с помощью eval , то писать компиляторы на питоне я никому не посоветую. Да, первый результат получился быстро, но отлавливать ошибки и поддерживать код очень сложно.
Первый эмулятор тоже был на питоне, но потом я его переписал на Rust, что ускорило его раз в пять.
Множество Мандельброта (скриншот из эмулятора)
Примечание
Временные диаграммы нарисованы в wavedrom, а блок-схемы в Inkscape. Электрические схемы – скриншоты из KiCAD.
📸 Видео
Очень важные параметры видеокарты, на которые редко обращают внимание при покупке!Скачать
ЗАПИСЬ и СВЕДЕНИЕ ДОМА НА БЕСПЛАТНЫЙ БИТ В FL STUDIO 20Скачать
КАК НАУЧИТЬСЯ ПИСАТЬ БИТЫ?Скачать
Что будет если налить КОЛУ на ВИДЕОКАРТУ?Скачать
Почему в видеокартах rx6800, rx6900 шина 256 бит?Скачать
КАК ПИСАТЬ БИТЫ В FL STUDIO НОВИЧКУ? / FL STUDIO 20 ЗА 5 МИНУТСкачать
СПОРИМ Я НАУЧУ ТЕБЯ ПИСАТЬ БИТЫ ЗА 18 МИНУТ? АЛГОРИТМ С НУЛЯ ДО ПРО! #BeatmakerWay 1Скачать
Учусь Писать Биты За 24 ЧасаСкачать
Как Всегда Идеально Сводить Биты ?? Пошаговая Инструкция Как Свести Бит + FREE DrumkitСкачать
КАК НАПИСАТЬ БИТ В FL STUDIO / СВЕДЕНИЕ И МАСТЕРИНГ!!!!Скачать
КАК СДЕЛАТЬ БИТ И ЗАПИСАТЬ ТРЕК НА ТЕЛЕФОНЕ В ПРОГРАММЕ BandLab | БИТ ДЛЯ НАЧИНАЮЩИХ НА АНДРОИД 2021Скачать
Почему писать PLUG — это очень просто.Скачать
КАК ПИСАТЬ ЖИРНЫЕ И КАЧЁВЫЕ БИТЫСкачать