Advanced: Тема повышенной сложности или важная.
Дoшли руки, благодаря этому, до первых практических схемотехнических шагов в симуляторе на пути к «x80».
Здeсь неполный набросок файла со схемой «контекста x80» и схемой ручного быстрого теста…
Вроде бы работает, как и планировал.
Полная схема из 256 ячеек в LogiSim слишком крепко тормозит. Потому имеются лишь ячейки ключевых регистров.
Почему-то графически с относительной лёгкостью набросал всё это.
А вот как описать в Verilog - пока не знаю. Видимо, нужно одну ячейку описать как примитив и сложить из них матрицу 16x16?
Хоть LogiSim очень удобный интерактивный инструмент, но производительность наихудшая.
Почему нельзя симуляцию производить прямо видяшкой?
Файл контекста (неполный) [LogiSim]
Alikberov
> Да и для ПЛИС не так дёшев…
ПЛИС не нужна для проектирования, инфа 100%. Скачай Icarus Verilog + GTKWave, пару книжек по, собственно, верилогу. Почитай туториалы marsohod.org
В итоге, у тебя будут исходники с проектируемым устройством + исходники с тестами, запускаешь симуляцию на Icarus, результаты смотришь через GTKWave. Тут, конечно, есть одна засада, - сам верилог своим синтаксисом позволяет несколько больше вольностей чем способны выполнить существующие CPLD/FPGA, поэтому полёт фантазии нужно проверять на синтезируемость, в IDE от разработчика конкретной ПЛИС микросхемы.
>ПЛИС не так дёшев…
5$ на 9000 логических ячеек. Практически даром. Дискретной 74й логики на эти деньги, максимум штук 20 микросхем, можно купить.
0iStalker
> Скачай Icarus Verilog + GTKWave
Нe первый год им и пользуюсь.
Это LogiSim я как-то пробовал и забыл, а сейчас - вспомнил.
Чем Atanua/LogiSim удобны - интерактивность: Всегда можно нажать нужную кнопочку или отследить сигнал.
А вот GTKWave - позволяет только изучать графики.
Тем более, Бейсик позиционируется как язык с минимальным входным порогом в школах за 4 часа. А я его самостоятельно изучал больше года!!!
А параллельно - составлял узлы цифровых схем в тетрадях…
То есть, схемотехнику освоил быстрее базовых основ программирования.
Я читал, что программисту сложнее освоиться в электронике, чем электронщику в программировании.
У меня наблюдается (не впервые) обратное. И мне сложнее описать всё на Verilog, чем накидать дискреткой тот же RISC. Который я сначала попытался эмулятором реализовать в JavaScript, но запарился и запустил Atanua, а потом вспомнил и LogiSim.
Раньше (лет 15 назад) использовал OrCAD 10, но там очень много глюков было в GUI и при построении схемы всё вылетало нафиг.
И опять таки: Atanua/LogiSim позволяют собачить схему в режиме реального времени, тогда как OrCAD и Verilog - инженерные инструменты и разработчик должен хорошо ориентироваться в графиках…
По теме
Сейчас не могу решить, каким вариантом заняться…
Вариант с огромным регистровым файлом позволяет выполнять несколько процессов и в эмуляторе я таким режимом кучу глюков поймал: Аппаратно переключить страницы контекстов несколько проще, тогда как в эмуляторе необходимо учитывать факт переключения контекстов и пересчитывать все зависимые локальные переменные. Тогда как в схеме всё это выполняется проводами и логикой без надобности перезагрузки и тактирования.
Вариант со стандартным регистровым файлом, как в i8080/z80. Машинные циклы будут очень длинными, что мне и не нравится. Получится схема «первый блин комом».
Если схема моего RISC тратит по 2 такта на обычную операцию, 3 - на условную и 4 - на память. То здесь, по моим подсчётам, до 20 тактов может уходить.
Если заняться первым вариантом, то можно построить всё на базе RISC со словом микрокода по 32 бита.
Тем самым, «MOV CL,[BP+4]» будет представлена примерно таким кодом:
-------- -------- -------- 10000000:IB -------- -------- -------- yyyyyyyy:<y> ;0…127 / -127…-1 00000000 -------- -------- --------:HALT ;Halt microcode 0xxx0000 -------- -------- --------:PREFIx;Select microcode page 10000000 -------- -------- 0-------:NOP 10000000 -------- -------- 1-------:DATA ;Read immediate data 1xxx0000 -------- -------- --------: xxxx0xxx -------- -------- yyyyyyyy:READ ;DST = [Ptr[x] + y] xxxx1000 -------- -------- --------:<ALUx>;DST = ALU[x](DST, SRC) xxxx1xxx -------- -------- yyyyyyyy:WRITE ;[Ptr[x] + y] = SRC -------- nnnnnnnn -------- --------:SRC[n] -------- -------- nnnnnnnn --------:DST[n] 11 A6 04 | MOV CL,[BP+4] --------- OFS:80 - Offset by 80 [IB] DST:C1 - Accumulator C1 [CL] PTR:E1 - Pointer by E1:E9 [BP] in Read Mode - Address = PTR + Offset HALT - Ready ========= 0110:10_??_??_??|PAGE 1 ++++ 1A60:80_??_??_80|DATA - IB = [IP ++] 1A64:E1_??_C1_80|READ - DST[C1] = [Ptr[E1] + IB] 1A68:00_??_??_??|HALT 11 B6 04 | MOV [BP+4],CL --------- SRC:C1 - Source by C1 [CL] OFS:80 - Offset by 80 [IB] PTR:E9 - Pointer by E1:E9 [BP] in Write Mode HALT - Ready ========= 0110:10_??_??_??|PAGE 1 ++++ 1A60:80_??_??_80|DATA - IB = [IP ++] 1A64:E9_C1_??_80|WRITE - [Ptr[E1] + IB] = SRC[C1] 1A68:00_??_??_??|HALT
Aккуратно нарисовал схему «парсера инструкций» процессора:
Если Вы подзабыли систему команд x80, то напомню:
Если Вы помните особенности системы команд, то не забудьте и про постфиксы:
77 BE FF|07 BE <FF>|07 - явный префикс 77 BE FE|07 BE <FE>|07 - явный префикс 7 77 BE FD|07 BE <FD>|07 - явный префикс 7 BE FD|08 BE <FD>|08 - префикс отсутствует BE FE|0E BE <FE>|0E - неявный префикс: постфикс 14 BE FF|0F BE <FF>|0F - неявный префикс: постфикс 15
Удивительнo, но схема декодирования входного потока x80-инструкций получилась компактной и относительно простой:
Слева - ПЗУ с тестовым набором кода.
Справа - ОЗУ предварительного кэша декодируемых инструкций.
(Естественно, кэша не будет. Там лишь копятся логи для этой гифки.)
В углу - текст дизассемблерного тестового кода.
Итак, проследим всю очередь:
0000 01 |MOV BH,[BX] |80101: 8xxxx - нет префикса 0001 0A |ADD AL,[BX] |80A0A: 8xxxx - нет префикса 0002 11 0A |ADD BH,[BX] |10A0A: 1xxxx - префикс #1 0004 A1 CE|MOV BH,0xCE |8A1CE: 8xxxx - нет префикса, есть константа 0006 11 A1 CE|MOV BH,[BP-27]|1A1CE: 1xxxx - префикс #1 и констанста 0009 BC FD|JC 0x0008 |8BCFD: 8xxxx - нет префикса, относительный условный переход 000B BC FE|CLC |EBCFE: Exxxx - постфикс #14 условного перехода с замыканием на себя 000D 11 BC FE|JC 0x020E |1BCFE: 1xxxx - префикс расширения диапазона относительного перехода
Под паяльник потребуется:
Тем самым, первый шаг по считыванию очереди (не памяти) машинного кода сделан.
Теперь нужно построить узел чтения/записи ОЗУ (аналогично этому) и можно начать строить дешифратор команд.
Получается 11 префиксов на 256 команд - 12 бит на декодер или адрес с микрокодом под одну из 2816 микропрограмм. Вполне реальная задача…
P.S.: Честно говоря, меня несколько запугали ужасами перехвата префиксов и ветвлений. Я опасался, что малой логикой не обойдётся это. Однако, за сутки проблема решена.
А учитывая недавний RISC-опыт, вполне удачный, готовые наработки можно копировать (всё своё) и реализовать CISC-инструкции с машинным циклом любой длины.
визуально на схеме есть комбинаторный путь через 5 элементов. большая часть логики (типа ЛА3) даёт задержку до 22 ns. всякие там ЛП5 - аж 30 ns.
ну и если выход идёт с триггеров типа TM2, то тот тоже тупит до 40 ns. вот и получается, что только у этого крошечного куска схемы максимальная тактовая будет 6.3 MHz
а стоит намалявать схему потолще - тактовая упадёт ещё ниже.
что мешает описать эту схему на верилоге, неясно.
книга, походу, не читана.
Mahagam
> что мешает описать эту схему на верилоге, неясно.
Тaм я зашёл в тупик же. В листинге видно.
(Например, одна и та же операция размазана различными циклами по всему тексту, что сильно путает и затрудняет разработку.)
А так как все школьные годы я занимался цифровухой (обычную радиоэлектроннику не понимаю), то мне визуально легче узлы отдельно отлаживать и компоновать в компактный прямоугольник, как признак того, что суваться туда больше не надо. И отдельные листы мозайкой собирать в одну схему.
Verilog я хоть освоил, но легче визуально охватывать именно схемы, чем листинг.
Хочу именно схемой попытаться понять, как одну операцию можно нормально реализовать одним цельным узлом, а не фрагментами по всему проекту.
Изучал Z80 файлы. Но вообще ничего не понял…
> большая часть логики (типа ЛА3) даёт задержку до 22 ns. всякие там ЛП5 - аж 30 ns.
> ну и если выход идёт с триггеров типа TM2, то тот тоже тупит до 40 ns.
Есть серия 531 и 1533 с более лучшими показателями.
И западная 74G тоже вполне доступна, если приспичит.
Главное, завести всё это хотя бы на 100 кГц. Чтобы со скрипом, но работало.
А потом и оптимизировать.
Кстати, у ЭСЛ выходы у всех элементов парные, с инверсией.
На ЭСЛ легче всего строить такие вещи. Но в моём регионе они в жутком дефиците из-за драгмета.
А у отца их - очень мало. И в основном, элементарная логика.
Искал 15 лет назад счётчики у барыг - не нашёл. Всё скупили граждане соседних республик.
> книга, походу, не читана.
В книге на схемах и отрабатывается.
А потом показывается, как описать в Verilog.
> визуально на схеме есть комбинаторный путь через 5 элементов.
Это я вижу. Потому и прикинул состав корпусов.
У меня есть трюк один: Подсчитываю число корпусов и умножаю на максимальную задержку тупо, будто всё последовательно включено. И так я прикидываю наихудший показатель быстродействия.
Если взять 11 корпусов по 40 нс, то получается 440 нс - чуть выше 2 МГц.
Пока - нормально…
Если опираться на таблицы и серию 1531, то ЛП5 сожрёт 6½ нс, ЛА3 - 5 нс, ЛЕ1 - 5½ нс, ЛН1 - 5 нс, ЛЕ7 - 6 нс, ЛА19 - 7½ нс, ТМ2 - 10 нс, ИР11 - 12 нс, ИР24 - 14 нс…
Но собирать из отборных корпусов - нереально (в моих условиях).
На данный момент
Готовый узел своего RISC для чтения памяти, ясное дело, не подходит здесь. Нужно строить абсолютно иной.
Быстрые наброски - не завелись.
Нужно париться над чтением байтов. А также и слов, для стека, с постинкрементом и преинкрементом.
А регистровый файл убивает тем, что SP и IP - там. И на счёт вверх/вниз нужно тратить такты для записи в этот файл.
> что мешает описать эту схему на верилоге, неясно.
Визуально сразу видно провод/шину и визуально легко отследить путь.
В тексте - как-то всё не очевидно. И комментарии не помогут.
Схема визуально легче охватывается.
Нету у меня чутья, что ли, профессионального в Verilog.
А с ТТЛ я могу работать даже без чертежа, если паять на коленке.
Это потом я буду переводить наработки схемы в текст Verilog…
P.S.: Спасибо за оценку и критику.
Одному сложно грехи выискивать…
Alikberov
> Тaм я зашёл в тупик же.
Скачай книгу Панчула. Там есть пример на Верилоге MIPS-подобного процессора с внеочередным исполнением и конвейером.
Адаптируй под свои нужды и получи профит.
kerosene
> Там есть пример на Верилоге MIPS-подобного процессора с внеочередным
> исполнением и конвейером.
> Адаптируй под свои нужды и получи профит.
Кaк говорится, легче с нуля своё написать под оригинальную задачу, чем перепиливать готовое и ловить глюки в неожиданных местах.
Эмулятор с ассемблером как-то месяц ковырял и пытался перелопатить под x80. Потом как-то тупо сел и за неделю своё с чистого листа написал.
У меня нет соответствующего образования, чтобы понимать чужой Verilog-код. Смотрю, и испытываю страх бабульки, чтобы где-то что-то не испортить и не поломать.
А своя схема - всегда в голове. И изложить её чертежом я могу десятком способов.
И MIPS - даже и не мой тот RISC, чтобы раскручивать его до CISC.
Только что завёл схему наконец-то и она читает код прямо из ОЗУ с инкрементом IP в контекстном файле:
На чтение восьми инструкций затратилось 32 такта.
Сейчас надо спать, а завтра - буду продолжать…
P.S.: Если подумать, то проект заманчив тем, что процессор должен будет уметь не лампочками мигать, а поддерживать CP/M-80…
Надo сказать, что на стр. 975 рис. 7.23 вижу то, что и накидал в своей схеме: Мультиплексор с константой перед сумматором.
Тут у меня проблема в том, что у мультиплексора всего лишь один вход селекции. То есть, лишь одно устройство может управлять выбором входа.
Ясное дело, что ни в одной номенклатуре не существует мультиплексора, которому можно ещё выбрать вход выбора входа. Здесь нужно подключить ещё один мультиплексор. Но получается дикая путаница. Что-то я несколько не туда двигаюсь.
Во-вторых, нужно добавить регистр машинных циклов с асинхронной установкой бит, но с синхронным их сбросом по ступеням. Здесь нужно применять ТМ2 - это очевидно, так как у него R-S входы асинхронны. Но вот синхронное снятие активных битов требует громадного количества логики.
На деле, любой узел может сделать установку нужного бита: «MC |= 1 << x». Здесь входы S снабжаем широким элементом ИЛИ и всё.
А вот выполнение синхронной функции «MC &= MC - 1» требует большого числа логических элементов, так как сумматор использовать я не хочу. А наброски - безобразны:
Но, становится очевидно, что на очередной ступени наращивать число входов ИЛИ для связи со всеми предыдущими ради быстродействия - нет смысла. Так как по мере обнуления и тактирования, этот узел не критичен к быстродействию и его можно оформить так:
Надеюсь, я не ошибаюсь…
Mahagam
> визуально на схеме есть комбинаторный путь через 5 элементов.
Пришлoсь временно забить на номенклатурную совместимость ТТЛ серии и оптимизировать.
Вроде бы удалось выровнять цепи и сократить их.
Теперь схемы две: Одна - для ТТЛ микросхем, вторая - для моделирования и оцифровки в Verilog…
Параллельно перечитываю книгу.
Отказаться в пользу мультиплексеров от буферов с Z-состоянием в регистровом файле - роскошь как графической перерисовки, так и ресурсов в общем.
Хотя LogiSim и поддерживает подключение пользовательских компонентов через Java-модули, но по факту - это хак. Правда, регистровый файл не мешало бы описать именно алгоритмически для большей производительности…
Весь вечер ушёл на оптимизацию этой комбинаторики.
Хочу отдохнуть…
Сeл и перевёл узел в модуль Verilog:
module x80_parser (input wire [7:0] DB // Data Bus ,inpur wire Super // Supervisor mode ,input wire Reset // ,input wire Clock // ,input wire Ready // Instruction complete ,output wire Wait // Waiting for instruction ,output reg [8:0] ID // Immediate Data ,output reg [8:0] IC // Instruction Code ,output reg [3:0] IB // Instrumental Buffer ); wire is_prefix = ~(~(Super | DB[2] | DB[1] | DB[0]) | (DB[2] ^ DB[6]) | (DB[1] ^ DB[5]) | (DB[0] ^ DB[4]) | DB[3] | DB[7] | IB[3] | IC[8]); wire is_postfix = (DB[7] & DB[6] & DB[5] & DB[4] & DB[3] & DB[2] & DB[1] & IC[5] & IC[4] & IB[3]); assign Wait = ~(IC[8] & ID[8]); // always @(negedge Clock) begin if(is_prefix | is_postfix) begin IB[3:0] = {~IC[8], DB[2:0]}; end if(~is_prefix & ~IC[8]) begin IC[8:0] = {1'b1, DB}; end if(~is_prefix & ~ID[8] & ~(~IC[8] & DB[7] & ~DB[6])) begin ID[8:0] = {1'b1, DB}; end end always @(Reset) begin IB = 4'b0000; IC = 9'b000000000; ID = 9'b000000000; end endmodule
P.S.: Модуль не тестировал пока и не уверен, что нет опечаток.
Каша комбинаторики просто подавляет!
Хотя графически схема выглядит стройной…
Оказываeтся, прошло пол года…
За два прошедших дня удалось (наконец-то) взяться за схему снова и построить примитивную «читалку»…
Теперь всё по правилам.
Из ОЗУ команды считываются READ-стробированием с растягиванием цикла чтения по READY (он всегда активен здесь) байт за байтом. Все байты команд накапливаются в регистрах IB, IC, ID около светодиода M1. Оттуда кодом производится выборка микрокода из ПЗУ слева внизу на 65536 32-битных слов. На одну команду может приходиться до 16 микрокоманд.
В данном примере на считывание 16 байтов (8 команд) и их исполнение (пока холостое) по 4 микрокоманды уходит всего 86 тактов. В среднем 11 тактов на команду - вроде нормально.
Сам я заметил, что на время выполнения микрокода адрес ОЗУ уходит в ноль довольно надолго. То есть, можно продолжать читать сами команды конвейером, добавив ещё регистров. Но, этого я делать пока не планирую, так как главное - построить CISC и заставить его работать, а не соревноваться с RISC-индустрией.
Основной узел я практически не изменил, а лишь под ним обвесил как попало логикой управления шиной. Слева очень много пустоты и там нужно много работать, добавив АЛУ и т.д…
P.S.: Просто проверял сам принцип, так как в Verilog у меня получается путаница, тогда как с графической схемой я теперь примерно вижу, как в Verilog красиво описать отдельные узлы…
Доработaл схемку.
Теперь она читает команды из ОЗУ и умеет выполнять одну единственную команду - «JC».
А так как модуль АЛУ и условий ещё отсутствует, то «JC» работает как «JMP» и прыгает с адреса 0x000E на 0x0006…
В сравнении со вчерашней схемой, я почистил левую часть и ПЗУ микрокоманд перенёс в конец, чтобы схема стала компактнее и красивее.
Внизу виден страшный лабиринт проводов, так как пришлось сильно поплясать в пошаговой отладке.
Справа два светодиода: «HALT» - конец микрокода; «UID» (Use Immediate Data) - использование константы…
На 7 команд уходит 35 тактов, так как все команды, кроме последней, холостят. На последнюю - 4 такта.
Получается, в среднем, по 5 тактов на команду.
Вполне ожидаемо, так как у i8080/z80 то же самое.
P.S.: Теперь нужно накидать блок АЛУ и набить 2816 команд…
Именно 2816 x80-команд, а не микрокоманд! O^O
Автоматически сгенерировать? Можно, но нужно JS-эмулятор ковырять свой.
Alikberov
> Автоматически сгенерировать? Можно, но нужно JS-эмулятор ковырять свой.
Примерно так и появился верилог со товарищи.