Компоненты и технологии, № 7'2002
Окончание. Начало в №3
Микропроцессор своими руками
Иосиф Каршенбойм
Начинаем проект в FPGA
При разработке и создании все более сложных и объемных цифровых схем появляется проблема повышения эффективности работы разработчиков и гибкости проекта, а основным требуемым критерием эффективности разработки является время, затраченное на разработку, или, иначе говоря, «time to market» — время от начала реализации проекта до выхода готового продукта на рынок. Поэтому, проект строится в виде иерархической пирамиды файлов. При таком подходе каждый «кирпич» пирамиды проектируется и отлаживается отдельно, что позволяет в дальнейшем пользоваться набором проверенных блоков, называемых библиотечными. Проект будем выполнять на языке AlteraHDL, так как он достаточно широко распространен и описан. Кроме того, для начинающих, он гораздо более доступен, чем другие языки группы VHDL. Так же более доступны и студенческие версии ПО.
Проект будет выполнен в виде текстовых файлов. Это позволит применить параметрическое выполнение описаний, что даст возможность применить файлы и в других проектах. В пакете ПО MaxPlus фирма Altera дает библиотеку параметризуемых мегафункций, позволяющих легко встраивать в проект счетчики, дешифраторы и пр. Описание этих мегафункций приведено в Help-файлах ПО.
Соглашение о названии сигналов (о наименовании цепей)
Для однообразия примем, что активное состояние сигналов будет равно 1. Если активное состояние сигнала будет равно 0, то в название сигнала на конце будет добавлена буква «я».
В файлах описаний применим следующие названия сигналов:
elk — частота, синхрочастота,
_епа — сигнал разрешения записи, в большинстве случаев его длительность будет равна длительности 1-го периода синхрочастоты, reset — сигнал системного сброса.
_load — сигнал загрузки,
_node — название сигнала относится к внутреннему узлу схемы,
_rg — название сигнала относится к внутреннему регистру
_cnt — название сигнала относится к внутреннему счетчику.
Все сигналы внутри FPGA Altera могут быть только однонаправленными, поэтому у каждого блока обычно есть входы и выходы данных, поэтому
й[], (1ма_а[], (1ма_Ъ[] — эти и другие подобные сигналы будут являться входными шинами данных для разрабатываемых блоков,
q[] — эти и другие подобные сигналы будут являться выходными шинами данных для разрабатываемых блоков (если это не оговорено отдельно).
Начинаем с разработки вспомогательных файлов.
Произведем разработку вспомогательных файлов. Начнем с регистра из Б-триггеров.
Далее приведен текст файла 0££ех_^Л^. Это регистр со входами сброса, установки и разрешения записи. Разрядность регистра задается в параметрах и, по умолчанию, принята равной 8 бит.
-- Version 1.0
-- Copyright Iosif Karshenboim, 2000
-- You may use or distribute this function freely,
-- provided you do not remove this copyright notice. -- If you have questions or comments, feel free to -- contact me by email at [email protected]
PARAMETERS ( m = 7, l = 0 );
SUBDESIGN Dffex_rg
( d[m..l] і INPUT;
clk і INPUT;
resn і INPUT = VCC ;
setn і INPUT = VCC ;
ena і INPUT = GND ;
q[m..l] і OUTPUT; )
BEGIN
FOR i IN l to m GENERATE
q[i] = DFFE(d[i], clk, resn, setn, ena ); END GENERATE;
Выберем в качестве устройства для проекта микросхемы серии ACEX или FLEX10, а далее выбор конкретного устройства установим в режим AUTO.
Создайте рабочую папку, например My_cpu, запишите файл в эту папку, затем выполните компиляцию, чтобы убедиться в отсутствии ошибок, и создайте инклюдный файл Dffex_rg.inc. Так же будем поступать и со всеми другими файлами проекта о которых будет говориться здесь.
Далее создедим еще один вспомогательный файл — mdffex_rg.tdf. Это регистр, аналогичный предыдущему, со входами сброса, установки и разрешения записи. Здесь уже используется предыдущий файл, как основа для построения данного фай-------www.finestreet.ru--------------------------
END
Компоненты и технологии, № 7'2002
ла. От предыдущего этот регистр отличается тем, что имеет на входе мультиплексор данных. Сигнал ва1 выбирает какую шину data_a[DATA_WIDTH-1..0] или data_b[DA-ТЛ_’ШОТИ-1..0] подключить ко входу регистра. Разрядность регистра задается в параметрах и, по умолчанию, принята равной 16 бит.
-- Version 1.0
-- Copyright Iosif Karshenboim, 2000
-- You may use or distribute this function freely,
-- provided you do not remove this copyright notice.
-- If you have questions or comments, feel free to -- contact me by email at [email protected]
INCLUDE «dffex_rg.inc»;
PARAMETERS (-- Параметры
DATA_WIDTH = 16 -- разрядность шины данных
SUBDESIGN mdffex_rg ( data_a[DATA_WIDTH-1..0], data_b[DATA_WIDTH-1..0], sel, clk, ena, reset q[DATA_WIDTH-1..0]
BEGIN
q[DATA_WIDTH-1..0] = dffex_rg (( !sel & data_a[DATA_WIDTH-1..0]) # ( sel & data_b[DATA_WIDTH-1..0]), clk, !reset, , ena ) WITH ( m = DATA_WIDTH-1, l = 0 ) ;
END;
: INPUT = GND ; : OUTPUT; )
димости, легко модернизировать микропроцессор.
Теперь создадим файл, описывающий стек.
Стек будет представлять набор регистров с мультиплексорами на входах. Регистры будут соединены каскадно. При этом, на входы регистра, через мультиплексор, начиная с 1-го, подаются выходы от регистра с меньшим номером или от регистра с большим номером. На вход 0-го регистра подается входная шина данных, а его выходы соединены с выходной шиной данных стека. Если на входах управления режимом работы блока — push и pop нет активного сигнала, то сохраняется предыдущее состояние. Если активен сигнал push, то в 0-й регистр записываются данные со входной шины данных, в остальные регистры записываются данные от «соседа» с меньшим номером и, следовательно, данные «проталкиваются» в стек, а если активен сигнал pop, то в регистр записываются данные от «соседа» с большим номером и данные извлекаются из стека.
Наш стек будет иметь параметры как по разрядности шины данных, так и по глубине. Такое выполнение файла позволит, при необхо-
-- Version 1.0
-- Copyright Iosif Karshenboim, 09.06.2000 -- You may use or distribute this function freely,
-- provided you do not remove this copyright notice.
-- If you have questions or comments, feel free to -- contact me by email at [email protected]
INCLUDE «mdffex_rg.inc»;
PARAMETERS (-- Параметры
STACK_WIDTH = 4, -- разрядность шины адресов памяти
команд в словах «CPU_WIDTH»
DATA_WIDTH = 24 -- разрядность шины данных памяти
команд
SUBDESIGN stack ( data[DATA_WIDTH-1..0], push, pop, clk q[DATA_WIDTH-1..0]
INPUT = GND OUTPUT; )
VARIABLE
s[DATA_WIDTH-1..0][STACK_WIDTH-1..0] : NODE;
BEGIN
s[DATA_WIDTH-1..0][0] = mdffex_rg
(.data_a[(data_width) — (1)..0] = data[DATA_WIDTH-1..0] , .data_b[(data_width) — (1)..0] = s[DATA_WIDTH-1..0][1] , .sel = pop , .clk = clk , .ena = push # pop )
WITH ( DATA_WIDTH = DATA_WIDTH );
FOR i IN 1 to ( STACK_WIDTH — 2 ) GENERATE s[DATA_WIDTH-1..0][i] = mdffex_rg ( .data_a[(data_width) —
(1)..0] = s[DATA_WIDTH-1..0][i-1] ,
.data_b[(data_width) — (1)..0] = s[DATA_WIDTH-1..0][i+1] , .sel = pop , .clk = clk , .ena = push # pop )
WITH ( DATA_WIDTH = DATA_WIDTH );
END GENERATE;
s[DATA_WIDTH-1..0][STACK_WIDTH-1] = mdffex_rg ( .data_a[(da-
ta_width) — (1)..0] = s[DATA_WIDTH-1..0][STACK_WIDTH-2] , .data_b[(data_width) — (1)..0] = GND ,
.sel = pop , .clk = clk , .ena = push # pop )
WITH ( DATA_WIDTH = DATA_WIDTH );
q[DATA_WIDTH-1..0] = s[DATA_WIDTH-1..0][0] ;
END;
Основа стека — это двухмерный массив регистров с мультиплексорами 8[ОАТА_”ШОТН-1..0][8ТАСК_”МОТН-1..0], обозначенный в фай-
ле как узел — NODE. Массив имеет ширину — DATA_WIDTH и глубину — STACK_WIDTH.
Для проверки работы стека был создан файл, аналогичный приведенному файлу stack, но для удобства проверки работы, выходы регистров, образующих стек были выведены как выходные шины — q0_[] ...q3_[].
Симуляция работы блока stack приведена на рис. 2.
Сначала выполняется команда push, данные помещаются в стек, затем подается команда pop и данные извлекаются из стека.
Создадим файл, описывающий счетчик команд.
Нам нужен счетчик, который мог бы загружаться при выполнении команд JMP, CALL, RET. Значение адреса, которым будет загружаться счетчик, подадим по входной шине данных. Счетчик должен считать «вверх». Счетчик также должен иметь входной сигнал разрешения счета, так как для некоторых операций счет будет запрещен. Для счетчика команд применим библиотечный элемент — lpm_counter. Зададим параметр по разрядности шины адресов памяти команд — PS_WIDTHAD.
INCLUDE «lpm_counter.inc»;
-- разрядность шины адресов памяти
PARAMETERS ( PS_WIDTHAD = 7); команд
SUBDESIGN ps_cnt
(
clk, reset,
data_in[PS_WIDTHAD -1..0], ps_cnt_load,
ps_cnt_ena : INPUT;
ps_addr[PS_WIDTHAD-1..0] : OUTPUT;
)
VARIABLE
ps_cnt : lpm_counter WITH (lpm_width = PS_WIDTHAD); BEGIN
-- Счетчик адресов памяти команд «PS» ps_cnt.clock = clk; ps_cnt.aclr = reset;
ps_cnt.data[PS_WIDTHAD-1..0] = data_in[PS_WIDTHAD -1..0]; -- программная установка
ps_cnt.sload = ps_cnt_load; ps_cnt.cnt_en = ps_cnt_ena;
ps_addr[PS_WIDTHAD-1..0] = ps_cnt.q[PS_WIDTHAD -1..0];
END ;
Name: Value: 40.0n* 80.0n$ Диаграмма работы стека 120.0m 140.0ns 200.0ns 240.0ns 280.0ns 320.0ns
Bg-Clk 0 і і 1 1 1 1 1 і і 1
dota[23..0] - 000011 X 000022 X 000033 X о о о о 000055 X о о о о о. о. X 000000
s&- push 1 L
B- pop 0 Г 1
m q0_[23..0] - 000000 ft 000011 _Д 000022 XX 000033 $)( 000044 Ж 000033 $ 000022 ft 000011 ft 000000
ql_[23..0] - 000000 JX ooooiі XX 000022 $ 000033 ж 000022 )й 000011 ft 000000
m q2_[23..0] - 000000 ж ооооп XX 000022 ш 000011 XX 000000
q3_[23..0] _ 000000 и 000011 XX 000000
Рис. 2 На диаграмм синхрочасто е изо та — бражены входы: clk, входная шина данных — data[], входы управления — push, pop. На диаграмме изображены выходы: выходные шины регистров, образующих стек q0_[] ..^3_[],
Компоненты и технологии, № 7'2002
Счетчик имеет входную шину данных, входы синхронной загрузки и разрешения счета. Выходы счетчика подключены к выходной шине блока. По умолчанию такой счетчик будет считать «вверх». Итак, счетчик адресов удовлетворяет поставленным требованиям.
Создадим файл, описывающий ALU — основу нашего микропроцессора.
Прежде чем приступить к описанию ALU, позволю себе небольшое отступление. Когда в известной басне слепых спрашивали, какой им представляется слон, то каждый из них давал свое описание. Так же происходит и с микропроцессорами. На вопрос, как и почему микропроцессоры работают, дилер из коммерческой фирмы не задумываясь ответит: «Потому что они импортные!». Программист начнет рассказывать об операционных системах, языках программирования, оптимизирующих компиляторах и т.д. Если же сейчас спросить нас с Вами в этой части статьи, то мы должны ответить: «Микропроцессор работает только потому, что есть генератор тактовой частоты, счетчик адресов памяти, сама память, кое-какие регистры, называемые «порты», а все остальное — дешифратор. Да и память, собственно, это тоже большой дешифратор». Так вот — лучшая часть этого дешифратора и называется ALU!
Наше ALU будет состоять из двух «логических» частей: первая будет вырабатывать сигналы управления для всего, что есть в данном проекте, а вторая будет коммутировать выходные шины блоков на общую внутреннюю шину микропроцессора.
Самый верхний приоритет в исполнении команд имеет вход запроса прерывания, и, если есть состояние IRQ=1, то необходимо выполнить переход по адресу вектора и сохранить адрес возврата в стеке. Если данную команду представить в виде элементарных команд, то она эквивалентна двум командам JMP <addr> и PUSH.
Если нет запроса прерывания, т.е. IRQ=0, то необходимо продешифрировать слово команды, поступившее с выходов блока PS. Биты [23..20] дадут поле кода операции, биты [19..16] — укажут на регистр — приемник информации, биты [3..0] — укажут на регистр — источник информации, биты [1З..0] — укажут на адрес поля Mem.
TITLE «CPU ALU» ;
-- Version 1.0
-- Copyright Iosif Karshenboim, 2002
-- You may use or distribute this function freely,
-- provided you do not remove this copyright notice.
-- If you have questions or comments, feel free to -- contact me by email at [email protected]
-- Имеет память команд — PS, память данных — DS, отдельные шины адресов команд и данных.
-- Стек аппаратный на регистрах только для адреса возврата, нет команд PUSH и POP
Код операции 4 бита, адрес регистра или условие перехода 4 бита, константа или данные 16 бит.
-- Описание команд
0 — NOP
1 — JMP < Y > - переход без условий
2 — CALL
3 — RET - возврат
8 — MOV Reg, Reg - загрузка в регистр-приемник данных
из регистра-передатчика
9 — MOV Reg, [Mem] - загрузка в регистр-приемник данных из памяти
- по непосредственному адресу
А — MOV [Mem], Reg - загрузка в память по непосредственному адресу данных
- из регистра-передатчика
B — LDI Reg < C > - непосредственная загрузка в регистр-
приемник данных из
- памяти команд по текущему адресу
Регистры имеют номера 0 — 15,
для записи в них есть сигналы wr_reg_ena[15..0],
регистр 0 — аккумулятор
регистр 1 — регистр ввода-вывода
регистр 2 — регистр ввода-вывода
регистр 3 — регистр ввода-вывода
%
PARAMETERS
(
-- Параметры «ALU», задаваемые по умолчанию PS_WIDTHAD = 7, -- разрядность шины адресов памяти
команд в словах «CPU_WIDTH»
DS_WIDTHAD = 7, -- разрядность шины адресов памяти
данных в словах «CPU_WIDTH»
PS_WIDTH = 24, -- разрядность шины данных памяти команд DS_WIDTH = 16, -- разрядность шины данных памяти команд
STACK_WIDTH = 8, STACK_DATA_WIDTH = 7,
IRQ_VECTOR = H»0» -- адрес вектора запроса прерывания
--//////////////////////////////////////////////////////////////////////////// CONSTANT VECTOR = IRQ_VECTOR;
--//////////////////////////////////////////////////////////////////////////// SUBDESIGN alu
(
-- «ALU»
-- выход от «ALU» data_out[15..0],
ps_cnt_data[PS_WIDTHAD-1..0], -- данные для загрузки в счетчик команд wr_reg_ena[1..0], ps_cnt_load, push, pop,
ds_wr_ena : OUTPUT ;
irq, -- сигнал запроса прерываний для «ALU», действует 1 clk -- выходы блоков — входы для АЛУ data_in[23..0], ds_q[15..0],
rg0_q[15..0], -- выходы регистров
rg1_q[15..0],
stack_q[PS_WIDTHAD-1..0],
ps_addr[PS_WIDTHAD — 1..0] -- выходы счетчика адресов
: INPUT = GND ;
ps_cnt_data_node[PS_WIDTHAD-1..0] : NODE ;
--/////////////////////////////////////////////////////////////////
-- ЗДЕСЬ НАЧАЛО
-- сначала выполняем обработку прерываний,
-- потом обрабатываеи операнд
--/////////////////////////////////////////////////////////////////
--/////////////////////////////////////////////////////////////////
-- Обработка прерываний --/////////////////////////////////////////////////////////////////
IF irq THEN
-- абсолютный переход без условий по адресу вектора запроса прерывания
ps_cnt_data_node[PS_WIDTHAD-1..0] = VECTOR; ps_cnt_load = VCC ;
-- в память записываем адрес возврата data_out[PS_WIDTHAD-1..0] = ps_addr[PS_WIDTHAD-1..0]; data_out[15..PS_WIDTHAD] = GND ; push = VCC ;
ELSE
-- Нет запросов прерываний, или запросы этого уровня уже обработаны !
-- обработка операнда
--///////////////////////////////////////////////////////////////////////////////// -- ОБРАБОТКА ОПЕРАНДА --///////////////////////////////////////////////////////////////////////////////// CASE data_in[23..20] IS
-- H»1» — JMP < Y > — переход без условий
WHEN 1 =>
-- выходы памяти команд, где находится абсолютный адрес перехода
ps_cnt_data_node[PS_WIDTHAD-1..0] = data_in[PS_WIDTHAD-
1..0];
ps_cnt_load = VCC ; — переход без условий
-- H»2» — CALL < Y > — вызов без условий
- состоит из двух действий
- JMP < Вектор >
- Push
— переход без условий
занесение в стек
WHEN 2 =>
-- выходы памяти команд, где находится абсолютный адрес перехода
ps_cnt_data_node[PS_WIDTHAD-1..0] = data_in[PS_WIDTHAD
-1..0];
ps_cnt_load = VCC ; -- переход без условий
-- в память записываем адрес возврата data_out[PS_WIDTHAD — 1..0] = ps_addr[PS_WIDTHAD -
1..0];
data_out[15..PS_WIDTHAD] = GND ; push = VCC ;
-- H»3» — RET
— возврат из прерывания
WHEN 3 =>
-- записываем абсолютный адрес возврата в счетчик адресов
BEGIN
%
Компоненты и технологии, № 7'2002
-- от выходов стека, где находится абсолютный адрес возврата из прерывания
-- или вызова подпрограммы
ps_cnt_data_node[PS_WIDTHAD-1..0] = stack_q[PS_WIDTHAD-
1..0];
ps_cnt_1oad = УСС ; -- переход без условий
pop = VCC ;
извлечь из стека
-- H»8» — Mov Reg, Reg — загрузка данных из регистра в регистр
0 !!!
-- будем считать, что адреса регистра-передатчика располо-
жены в битах [3..0] слова
-- а адреса регистра-приемника расположены в битах [19..16]
слова
WHEN 8=>
CASE data_in[19..16] IS
WHEN 0 =>
CASE data_in[3..0] IS
WHEN 0 =>
data_out[15..0] = rg0_q[15..0]; -- выходы регистра 0
WHEN 1 =>
data_out[15..0] = rg1_q[15..0]; -- выходы регистра 1
END CASE;
wr_reg_ena[0] = VCC;
WHEN 1 =>
CASE data_in[3..0] IS
WHEN 0 =>
data_out[15..0] = rg0_q[15..0]; -- выходы регистра 0
WHEN 1 =>
data_out[15..0] = rg1_q[15..0]; -- выходы регистра 1
END CASE;
wr_reg_ena[1] = VCC;
END CASE;
-- H»9» — Mov Reg, [Mem] — загрузка в регистр-приемник дан-
ных из памяти
WHEN 9 =>
CASE data_in[19..16] IS
WHEN 0 =>
data_out[15..0] = ds_q[15..0]; -- выходы памяти данных
wr_reg_ena[0] = VCC;
WHEN 1 =>
data_out[15..0] = ds_q[15..0]; -- выходы памяти данных
wr_reg_ena[1] = VCC;
END CASE;
-- H»A» — Mov [Mem], Reg — загрузка данных из регистра-пе-
редатчика в память
WHEN H»A» =>
CASE data_in[3..0] IS
WHEN 0 =>
data_out[15..0] = rg0_q[15..0]; -- выходы регистра 0
WHEN 1 =>
data_out[15..0] = rg1_q[15..0]; -- выходы регистра 1
END CASE;
ds_wr_ena = VCC;
-- H»B» — LDI Reg < C > — непосредственная загрузка
-- в регистр-приемник данных из памяти команд по текущему
адресу
WHEN H»B» =>
CASE data_in[19..16] IS WHEN 0 =>
data_out[15..0] = data_in[15..0]; -- выходы памяти команд
wr_reg_ena[0] = VCC;
WHEN 1 =>
data_out[15..0] = data_in[15..0]; -- выходы памяти команд
wr_reg_ena[1] = VCC;
END CASE;
END CASE; END IF;
-- Шина данных на счетчик адресов памяти команд «PS» ps_cnt_data[PS_WIDTHAD-1..0] = ps_cnt_data_node[PS_WIDTHAD-
1..0] ;
END;
Создадим файл программы, которую будет выполнять микропроцессор.
Для описания памяти команд применим библиотечную функцию lpm_rom, и для ее инициализации применим файл step1.mif. Файл step1 будет содержать коды команд микропроцессора, требуемые для отладки проекта.
Чтобы нам было легче ориентироваться в диаграмме временной симуляции проекта, создадим несколько файлов инициализации памяти команд, причем каждый файл будет проверять работу не более чем одной или двух команд микропроцессора.
Файл step1 будет проверять работу команд NOP и JMP. Коды команд и их мнемоника приведены в таблице 1. По этой программе микропроцессор должен выполнить два шага и вернуться в нулевой адрес. Далее процесс доолжен повторяться циклически.
Для проверки микропроцессора по программе Step1, создадим файл инициализации памяти программ микропроцессора step1.mif
WIDTH = 24;
DEPTH = 128;
ADDRESS_RADIX = HEX;
DATA_RADIX = HEX;
CONTENT BEGIN
[0..1] : 000000;
% NOP %
2: 100000;
% JMP <000000> %
[3..7f] : 000000;
% NOP %
END;
Создадим файл, описывающий микропроцессора. Верхний файл проекта.
Теперь мы можем собрать верхний файл проекта — непосредственно микропроцессор. В микропроцессор входят файлы:
1. dffex_rg — этот файл будет выполнять все функции, где необходимы регистры: порт ввода-вывода, DS и др.,
2. stack — этот файл будет выполнять функцию стека,
3. ps_cnt — этот файл будет выполнять функцию счетчика адресов памяти команд,
4. alu — этот файл будет выполнять функцию АЛУ,
З. для описания памяти команд применим библиотечную функцию lpm_rom, и для ее инициализации применим файл stepl.mif. Далее приведен текст файла описания микропроцессора uP.tdf
Остальные команды микропроцессора могут быть проверены аналогично. Подробное описание работы с этой программой будет приведено в разделе, описывающем этап симуляции работы микропроцессора.
TITLE «Тестовый проект микропроцессор — uP» ;
-- Version 1.0
-- Copyright Iosif Karshenboim, 2002
-- You may use or distribute this function freely,
-- provided you do not remove this copyright notice.
-- If you have questions or comments, feel free to -- contact me by email at [email protected] %
-- Описание команд -- Код операции 4 бита.
0 — NOP
1 — JMP < Y > - переход без условий
2 — CALL
3 — RET - возврат
8 — MOV Reg, Reg - загрузка в регистр-приемник данных
из регистра-передатчика
9 — MOV Reg, [Mem] - загрузка в регистр-приемник данных из памяти по непосредственному адресу
А — MOV [Mem], Reg - загрузка в память по непосредственному адресу данных из регистра-передатчика B — LDI Reg, < C > - загрузка в регистр-приемник констан-
ты — данных из памяти команд по текущему адресу
Регистры имеют номера 0 — 1З, для записи в них есть сигналы wr_reg_ena[15..0], регистр 0 — аккумулятор регистр 1 — регистр ввода-вывода регистр 2 — регистр ввода-вывода %
INCLUDE «stack.inc»;
INCLUDE «dffex_rg.inc»;
INCLUDE «alu.inc»;
INCLUDE «ps_cnt.inc»;
INCLUDE «lpm_rom.inc»;
PARAMETERS
(
-- Параметры «ALU»
PS_WIDTHAD = У, -- разрядность тттнныт адресов памяти команд в словах «CPU_WIDTH»
Таблицаї. Коды, выполняемые микропроцессором, по программе step!
Адрес Код Мнемоника Комментарий
0000 000000 NOP Нет операции, переходим в следующий адрес
0001 000000 NOP Нет операции, переходим в следующий адрес
0002 100000 JMP<000000> Безусловный переход по адресу 000000
0003 000000 NOP Нет операции, переходим в следующий адрес
Компоненты и технологии, № 7'2002
PS_WIDTH = 24, -- разрядность шины данных памяти команд Б5_'^ОТН = 16, -- разрядность шины данных памяти команд
STACK_WIDTH = 8,-- глубина вложений в стек
IRQ_VECTOR = Н»0», -- адрес вектора запроса прерывания
PS_FILE = <^ер1.ші£» -- это файл команд микропроцессора -- PS_FILE = <^ер2.ші£»
CONSTANT STACK_DATA_WIDTH = PS_WIDTHAD; -- разрядность стека равна разрядности шины адресов памяти команд
--iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii SUBDESIGN uP
(
-- системные сигналы clk, reset,
-- сигнал запроса прерываний для «ALU», действует 1 clk irq : INPUT = GND ;
ps_cnt_load адресов
upalu_data_in[PS_WIDTH-1..0] і OUTPUT ;
OUTPUT ; -- загрузка счетчика
rg0_q[15..0], rg1_q[15..О]
-- выходы регистров
і OUTPUT ;
ps_cnt_node[PS_WIDTHAD-1..0],
rg0_q_node[15..0],
rg1_q_node[15..0] і NODE ;
upalu і alu WITH ( -- Параметры «ALU»
PS_WIDTHAD = PS_WIDTHAD, -- разрядность шины адресов памяти команд в словах
PS_WIDTH = PS_WIDTH, -- разрядность шины
данных памяти команд
DS_WIDTH = DS_WIDTH, -- разрядность шины
данных памяти данных
STACK_WIDTH = STACK_WIDTH, STACK_DATA_WIDTH = STACK_DATA_WIDTH,
IRQ_VECTOR = IRQ_VECTOR ); -- адрес вектора запроса прерывания
-- Здесь ко микропроцессора подключен вход запроса прерывания upalu.irq = irq;
-- Здесь ко входу АЛУ подключен счетчик адресов PS upalu.ps_addr[PS_WIDTHAD-1..0] = ps_cnt_node[PS_WIDTHAD-1..0];
-- Здесь ко входу АЛУ подключен регистр как память данных DS upalu.ds_q[DS_WIDTH-1..0] = dffex_rg ( upalu.data_out[DS_WIDTH-
1..0], clk, !reset, , upalu.ds_wr_ena )
WITH ( m = DS_WIDTH-1, l = 0 ) ;
-- Здесь ко входу АЛУ подключены два регистра как порты ввода-вывода
-- считаем, что порты имеют ту же разрядность, что и память данных
rg0_q_node[15..0] = dffex_rg ( upalu.data_out[DS_WIDTH-1..0], clk, !reset, , upalu.wr_reg_ena[0] )
WITH ( m = DS_WIDTH-1, l = 0 ) ; upalu.rg0_q[DS_WIDTH-1..0] = rg0_q_node[15..0]; rg0_q[15..0] = rg0_q_node[15..0];
rg1_q_node[15..0] = dffex_rg ( upalu.data_out[DS_WIDTH-1..0], clk, !reset, , upalu.wr_reg_ena[1] )
WITH ( m = DS_WIDTH-1, l = 0 ) ; upalu.rg1_q[DS_WIDTH-1..0] = rg1_q_node[15..0]; rg1_q[15..0] = rg1_q_node[15..0];
-- Здесь ко входу АЛУ подключен Стек upalu.stack_q[PS_WIDTHAD-1..0] =
stack ( .data[PS_WIDTHAD-1..0] = upalu.data_out[PS_WIDTHAD-
1..0] ,
.push = upalu.push, .pop = upalu.pop, .clk = clk )
WITH ( STACK_WIDTH = STACK_WIDTH , data_width = STACK_DATA_WIDTH );
-- Здесь сам счетчик адресов PS
ps_cnt_node[PS_WIDTHAD-1..0] = ps_cnt ( .clk = clk, .reset = reset, -- данные для загрузки в счетчик команд .data_in[PS_WIDTHAD-1..0] = upalu.ps_cnt_data[PS_WIDTHAD-
1..0],
.ps_cnt_load = upalu.ps_cnt_load, .ps_cnt_ena = VCC ) WITH (PS_WIDTHAD = PS_WIDTHAD );
-- Здесь ко входу АЛУ подключена память команд — PS как асинхронный блок
upalu.data_in[PS_WIDTH-1..0] = lpm_rom ( .address[PS_WIDTHAD-
1..0] = ps_cnt_node[PS_WIDTHAD-1..0],
.memenab = VCC )
WITH (LPM_WIDTH = PS_WIDTH, -- разрядность шины данных памяти команд
LPM_WIDTHAD = PS_WIDTHAD, -- разрядность шины адресов памяти команд
LPM_FILE = PS_FILE,
LPM_ADDRESS_CONTROL = «UNREGISTERED», LPM_OUTDATA = «UNREGISTERED» );
-- Все, что здесь написано нужно только для отладки ps_cnt_load = upalu.ps_cnt_load;
upalu_data_in[PS_WIDTH-1..0] = lpm_rom ( .address[PS_WIDTHAD-
1..0] = ps_cnt_node[PS_WIDTHAD-1..0],
.memenab = VCC )
WITH (LPM_WIDTH = PS_WIDTH ,-- разрядность шины данных памяти команд
LPM_WIDTHAD = PS_WIDTHAD, -- разрядность шины адресов памяти команд
LPM_FILE = PS_FILE,
LPM_ADDRESS_CONTROL = «UNREGISTERED», LPM_OUTDATA = «UNREGISTERED»);
В качестве DEVICE для проекта — выберем серию ACEX, а для выбора микросхемы установим режми AUTO. Произведем компиляцию проекта. Далее, создадим файл симуляции проекта микропроцессора с файлом команд step1, такой как на рис. 3. И произведем симуляцию проекта.
Синхрочастота, поступающая на тактовый вход микропроцессора, имеет длительность импульса 2О нсек и каждый положительный фронт синхрочастоты пронумерован. Все описание работы будет опираться на соответствующий фронт синхрочастоты. Будем называть фронты такі фронт1, фронт2 и т. д.
При действии фронта1 микропроцессор находится в состоянии сброса. Счетчик адреса обнуляется и из PS извлекается первая команда NOP, и никаких изменений не происходит. Под фронт2 и фронт3 из PS так же извлекаею-тя команды — NOP. Таким образом выполняются строки 0..1 из файла stepl. Когда счетчик адреса памяти команд находится в состоянии
2, из памяти команд извлекается команда, имеющая код 1ООООО. Это команда — JMP <000000>. Получив код этой команды, АЛУ производит дешифрацию кода операции, и выставляет сигнал на запись нового адреса в счетчик команд. Код адреса из АЛУ подается на счетчик команд. Под фронт4 производится запись в счетчик команд нового адреса. Далее
Диаграмма работы микропроцессора с файлом stepl
Name: Value: 10.0ns 20.0ns 30.0ns 40.0ns 50.0ns 60.0ns 70.0ns 80.0ns 90.0ns 100.0ns 110.0ns 120.0ns 130.0ns
IBS- reset 0
ES- irq 0
J& 1 2 3 4 5 6 7
■e— clk 0 1 1 1 I 1 1 1 1 1 1 1 1 1
-f_. ps_cnt_load 0 1 1 1
Щ_/ и pal u_data Jn [23. .0] H 000000 000000 X О о о о о X 000000 X 100000
ps_addr[6..0] H00 00 о X X 02 X 00 X 01 X 02 X
Рис. 3 Здесь файл Sim_step1.bmp На диаграмме изображены входы:
сброс — reset, запрс прерывания — irq. синхрочастота — clk,
На диаграмме изображены выходы:
сигнал разрешения записи в счетчик команд — ps_cnt_loаd, шина команд — upalu_data_in[23..0], шина адресов памяти команд — ps_addr[6..0].
END
BEGIN
Компоненты и технологии, № 7'2002
на шинах адреса устанавливается новый адрес.
По этому адресу извлекается новый код команды. Поскольку мы выполняли команду JMP <000000>, то микропроцессор перешел в адрес 0000 и на этом программа зацикливается в соответствии с файлом stepl. Результат симуляции показывает, что наш микропроцессор успешно выполнил команды NOP и JMP.
Далее читатели могут самостоятельно выполнить подобные действия для проверки работы микропроцессора с другими командами.
Для полной проверки микропроцессора, создадим файл инициализации памяти программ микропроцессора step2.mif. В этом файле будут проверяться все команды микропроцессора и так же будет проверяться работа по прерываниям. Произведем компиляцию проекта с данным файлом инициализации памяти.
WIDTH = 24;
DEPTH = 128;
ADDRESS_RADIX = HEX;
DATA_RADIX = HEX;
CONTENT BEGIN
[0..2] і 000000;
З і 200010; % CALL <0010> %
[4..6] і 000000;
7 і 100000; % JMP <0000> %
[8..F] і 000000;
10 і B055AA; % LDI Reg0, < 55AA > %
11 і B^CC; % LDI Reg1, < ЗЗ^ > %
12 і 810000; % MOV Reg1, Reg0 %
1З і A10000; % MOV [Mem], Reg1 %
14 і B01111; % LDI Reg0, < 1111 > %
15 і 900000; % MOV Reg0, [Mem] %
16 і 000000; % NOP %
17 і З00000; % RET %
[18..7F] і 000000;
END;
Создадим новый файл симуляции проекта.
Временная диаграмма симуляции работы микропроцессора с файлом команд 81ер2 приведена на рис. 4.
Синхрочастота, поступающая на тактовый вход микропроцессора, также как и в предыдущем случае имеет длительность импульса 20 нсек и каждый положительный фронт син-
хрочастоты пронумерован. При действии фронта1 микропроцессор находится в состоянии сброса. Счетчик адреса обнуляется и из PS извлекается первая команда NOP и никаких изменений не происходит. Под фронт2, фронтЗ и фронт4 из PS так же извлекаеютя команды — NOP. Таким образом выполняются строки 0.. 2 из файла step2. Счетчик команд находится в состоянии З и из этого адреса извлекается код команды 200010, что соответствует команде CALL <0010>. Под фронтЗ АЛУ формирует импульс записи нового адреса в счетчик команд и следующий адрес будет 10. Так же под фронтЗ производится запись в стек адреса возврата, на выходной шине стека появляется значение З. Под фронт6 начинает выполняться подпрограмма, см. файл step2, с адреса 10. Здесь выполняется команда LDI Reg0, < 55AA >. АЛУ формирует строб записи в регистр 0. Данные на вход регистра поступают по внутренней шине микропроцессора. Аналогично под фронт7 выполняется команда LDI Reg1, < ЗЗ^ >. Под фронт8 выполняется команда MOV Reg1, Reg0. Под фронт9 выполняется команда MOV [Mem], Regl. Под фронт10 выполняется команда LDI Reg0, < 1111 >. Эта команда нужна нам для того, чтобы показать, что следующая за ней команда MOV Reg0, [Mem], выполняемая под фронт11 снова изменит содержимое этого регистра. Под фронт12 выполняется команда NOP. Под фронт1З из памяти команд выбрана команда RET и выполняются следующие дей-ствияі из стека извлекается адрес возврата и заносится в счетчик команд. Стек выполняет команду POP и на его выходе значение с З меняется на 0, т. е. стек приходит в то состояние, которое у него было до вызова подпрограммы, см фронт0..4. Далее происходят следующие действияі под фронт14 микропроцессор вернулся в адрес З и снова начал выполнять вызов подпрограммы по адресу 10. Но под фронт16 на входе АЛУ появился сигнал запроса прерывания irq. По этому сигналу производятся действия, аналогичные действиям по команде CALL <0000>. АЛУ формирует импульс записи нового адреса — адреса вектора прерывания в счетчик команд и следующий адрес будет 0. Так же
под этот фронт производится запись в стек адреса возврата, на выходной шине стека появляется значение 11, а предыдущее значение стека — 3 будет «протолкнуто» внутрь стека. Далее микропроцессор выполняет команды, которые будут извлекаться из Р8 из адресов, по которым выполнен переход, по адресу вектора прерывания. Таким образом, мы видим, что микропроцессор выполняет все команды в соответствии с заданием
Несколько слов об отладке программ во встроенных микропроцессорах.
Во-первых, используемые микросхемы БРОЛ — перепрограммируемые, то есть в случае обнаружения ошибки всегда есть возможность исправить эту ошибку без переделки всего устройства.
Во-вторых, при отладке программы во встроенных микропроцессорах есть много дополнительных аппаратных возможностей, используя которые, можно достичь быстрого положительного результата.
Поскольку весь проект «самодельного» микропроцессора для нас открыт, и мы знаем его устройство, то мы можем облегчить отладку программ, при помощи дополнительных аппаратных ресурсов, так как в процессе отладки можно использовать БРОЛ с большим количеством ячеек, чем нужно для функционирования проекта. Это позволит выделять отладочные ресурсы. К таким ресурсам можно отнести:
A) дополнительные поля памяти для вызова тестовых и мониторных программ,
Б) тестовые входы и выходы для микропроцессора, позволяющие снимать состояния сигналов и задавать воздействия,
B) дополнительные поля памяти могут использоваться для включения в систему встроенных логических анализаторов см. Л15, что является очень мощным средством отладки,
Г) дополнительные счетчики, тестовые триггеры и т. д.
Далее, ко встроенным микропроцессорам может быть «пристроен» блок, выполняющий функции внутрисхемного эмулятора. С его помощью пользователю становятся доступны внутренние регистры процессора, осуществляется останов по адресу, шаговый режим и пр.
Name: Value: 20.0ns 40.0ns 60.0m 80.0ns 100.0ns 120.0м 140.0ns 160.0ns 180.0n* 200.0ns 220,0ns 240.0ns 260.0ns 280.0ns 300.0ns 320.0ns 340.0ns
is>- reset 0 n
IS— irq 0 1 1
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
^ rg0_q[15..0] ноооо 0000 —XX 55AA XK 1111 ft 55AA
rgl_q[15..0] ноооо 0000 Ж 33CC JH 55AA
iff wr_reg_ena[1..0] но 0 x і к і 2 і о її , ж о х і хк о т 0
^ ds_q[15..0] ноооо oooo X 55AA
*tack_q[6..0] ноо 00 X о о о X о U 19
-!—• p*_cnt_load 0 III 1 II II 1 1
Щ_/ upalu_data_in[23..0] Н000000 000000 X 200010 X В055ААІ В133СС X 810000 X АЮОО-зХ ВОШІ X 900000 X 000000 X 300000 X 200010 X В055АА I В133СС X
jg))t ps_addr[6..0] ноо 00 X 01 X 02 X 03 X ю X и X 12 X із X М X 15 X U X 17 X оз х ю І 11 X оо X оі X
Рис. 4 Здесь файл На диаграмме изоб сброс — reset, запрс прерывания синхрочастота — с Sim_ste ражен - irq. k, 11 2 в ^ s На диаграмме изображены выходы: выходные шины регистров гд0_я[15..0] и гд1_я[15..0], сигналы разрешения записи в регистры — wr_rg_ena[1..0], выходная шина регистра — ds_q[15..0], выходная шина стека — з1оск_я[], сигнал разрешения записи в счетчик команд — рэ. шина команд — ира1^а[а_т[23..0], шина адресов памяти команд — ps_addr[6..0] .cntjoad,
Компоненты и технологии, № 7'2002 Компоненты
Все эти возможности позволяют проводить успешную отладку программы не только в программном симуляторе, но и на «живом железе».
Что мы получили.
По итогам компиляции проекта, из файла рапорта, получаем следующий результат:
Тестовый проект микропроцессор — uP ** DEVICE SUMMARY **
Chip/ Input Output Bidir Memory Memory LCs
POF Device Pins Pins Pins Bits %Utilized LCs %Utilized
up EPF10
K30ETC144-13 57 0 6144 25% 182 10%
User Pins: 3 57 0
Для реализации проекта потребовалось всего 182 ячейки, при том, что проект содержит три 16-битных регистра.
Сравнительная характеристика.
Для сравнения можно сказать, что широко известные микропроцессоры, такие как МС851 выполняют одну команду за 12 тактов, а их более «продвинутые» версии — за 4 такта, и только новейшие СУОЫЛЬ за 1 такт. Чтобы сравнить производительности различных микропроцессоров с тем, что мы разработали, воспользуемся диаграммой, приведенной вЛ16, и добавим к ней данные по нашему проекту (см. рис. 5).
Заключение
Мы получили 16-битный ШвС-микропро-цессор, работающий на частоте 50 МГц, и вы-
полняющий все команды за один такт. И, следовательно, спроектированный микропроцессор имеет производительность 50 MIPS. Данный проект может послужить основой для разработки микропроцессора с требуемым набором команд и, с требуемыми для вычислений, ресурсами. На основе такого микропроцессора может быть разработан микроконтроллер, путем добавления к проекту блоков периферийных устройств, из имеющихся библиотечных мегафункций. Проект имеет все возможности, чтобы его можно было легко доработать до реальной конструкции. Конечно, при значительном увеличении числа команд сложность АЛУ тоже возрастет, а быстродействие несколько уменьшится. Однако применение конвейера команд позволяет преодолеть эти трудности. Но конвейер команд, тайм-слоты задерки переходов и прочие профессиональные «хитрости» выходят за рамки данной статьи. МММ
Литература
1. Дмитрий Фомин «Кремний на бумаге» или fabless?
Живая элекфоника pоссии, 2001, том 1 http://www.elcp.ru
2. А.П. Антонов Язык Описания цифровых устройств AlteraHDL. М. РадиоСофт, 2002.
3. И.Кривченко. Системы на кристалле: общее представление и тенденции развития. Компоненты и технологии, №6 2001.
4. Processors drive (or dive) into programmable-logic devices. By Markus Levy. EDN От 07.20.2000
5. Каршенбойм И.Г. Микроконтроллер для встроенного применения — NIOS. Кон-
Сравнение пиковой производительности
Разработанный Cygnal Microchip Philips ADuC812
микропроцессор CIP-51 PIC17C75x 60C51 8051
50 МГц [25MHz elk) (33MHz elk) [33MHz elk) (16MHzclk)
Рис. 5
фигурация шины и периферии. «Компоненты и технологии» №2, 3, 4, 5 2002
6. Altera™. Nios Soft Core Embedded Processor, data sheet June 2000, ver.1
7. Altera™. Nios_2_0_CPU_datasheet
8. Altera™. Nios 32-Bit Programmer's Reference Manual January 2002 Version 2.0
9. Altera™. Nios 16-Bit Programmer's Reference Manual January 2002 Version 2.0
10. Altera™. ARM-Based Embedded Processor Device Overview, data sheet February 2001, ver.1.2
11. Altera™. Nios_tutorial.pdf
12. Altera™. Nios_an188_Custom_Instructions
13. Altera™. Simultaneous Multi-Mastering with the Avalon Bus
14. И. Каршенбойм, Н. Семенов Микропро-граммые автоматы на базе специализированных ИС. — Chip News, №7, 2000.
15. Каршенбойм И.Г. «Встроенный» логический анализатор — инструмент разработчика «встроенных» систем. Схемотехника №12, 2001.
16. 8051CYGNAL.pdf