Написал резюме и расплакался... Я такой классный!...

Меню навигации для мобильных

Запустить 8086

Автор Slabovik, 03 Май, 2024, 16:43

« предыдущая - следующая »

Slabovik

Давно колупаюсь, когда время есть, но до практики так и не добрался ввиду объёмных затрат на хардварь. Тем не менее, давно хотел сделать какой-нибудь контроллер на базе этого проца. Да, есть NEC V40, но это уже перебор :)

Итак, имеем i8086 либо подобный. Задача-минимум - запустить. Задача-максимум - с прозрачным режимом работы с памятью и собственным видеоконтроллером, дающим картинку на телевизор либо LCD. 256 цветов достаточно. 16 - минималка.

8086 есть в нескольких вариантах. 8086 - базовый с тактовой 5МГц. 8086-2 имеет тактовую 8МГц. И есть редкий 8086-1 с тактовой 10МГц. Базовый купить полный кулёк не проблема, их много продают с разборок. 8086-2 надо поискать. 10-мегагерцовый вариант я не находил. А есть ещё вариант 80С86 - то же самое, только по КМОП-технологии.

Для начала надо разобраться с тактированием. Оно по-интеловски слегка странное - соотношение полупериодов должно быть 3/5 и 2/5

8086_timing_diagram.png

8086_timing_table.png

Тайминги версии 80C86 идентичны таймингам оригинала

Да, я видел варианты, где делают 2/3 и 1/3 и даже 3/4 и 1/4, но это с пониженными частотами, 4 а не 5, 6 а не 8...
Для 8-мегагерцового 1/3 при 125нс периоде получается только 41нс, а не 50, требуемых по даташиту (45+фронт)

3/5 и 2/5 можно чётко получить при 40-мегагерцовом осцилляторе

8086_40MHz_to_8_divider.png

Здесь подаваемая на процессор частота CT1, другие сигналы полезные для синхронизации узлов.

уфф, оказалось, у меня потерялась циклограмма. Потребуется время нарисовать по-новой... продолжу...
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Slabovik

Продолжим. 40МГц тактирования позволяет удобно определить цикл шины памяти с 25-наносекундным дедтаймом. Он нужен изза того, что память не очень быстро снимает данные со своего выхода после цикла чтения, и если следующим будет идти цикл записи, там целых полтора-два десятка наносекунд может быть игра "перетяни шину". Чтобы этого не было, данные нужно выставлять слегка позже, чем начинается цикл.

Слегка изменил схему генератора

8086_Gena_TM8_40MHz.png

По-идее, можно обойтись без  четвёртого (D3/Q3) триггера, но его девать некуда - пусть стробирует гейт на выводы 'L' триггеров ИР22, которые забирают данные из памяти.

Строб гейтов данных на запись в память выглядит точно также, только следует чуть позже за счёт задержки в логическом элементе.
Постарался наиболее точно отобразить задержки в элементах. В основу взял серию 74AC

8086_Gena_TM8_40MHz_timings.png

Можно частоту делить на 3, взяв в качестве опорной 24МГц. Это проще. Но поделив на D-триггерах, получим ровно 1/3, получим 41нс  полутакта, а по документам надо 50.

Можно попробовать посчитать делитель на 3 на JK-триггерах. Это асинхронные триггеры и фронты будут за счёт задержек смещены, возможно, в нужную сторону. Посмотрим. Вот схема делителя

8086_Gena_TB15_24MHz.png

Пойду тайминги рисовать :)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Slabovik

Что-то трудно с этим генератором. Не, на 3 делит чётко. Но задержками играть получается только на TTL сериях, у которых переход от 1 к 0  и от 0 к 1 происходит за разное время. У серии 74AC разница весьма незначительна, поэтому растянуть второй полупериод так просто не получается.

8086_24MHz_to_8_JK_divider.png

Слегка изменил положение строба шины

8086_24MHz_to_8_JK_divider_timings.png

Но по всему выходит и формирование строба чуток кривое - возможен глитч и без дополнительного обвеса его не побороть, и второй полупериод такта процессора коротковат от даташита.

Но с другой стороны я смотрю даташит на его родной геренатор. Там делитель на 3, вот в чём прикол!

82c84A_internal.png
ЦитатаThe CLK output is a 33% duty cycle clock driver designed to
drive the 80C86, 80C88 processors directly.
И, как такое бывает?

Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Slabovik

Думаю, надо пока закончить с генератором. Вот три варианта

Первый - делитель на три на основе JK-триггеров

8086_Gena_TB15_24MHz.png

Сигнал MASTB вспомогательный, по его восходящему фронту и до конца цикла формируется сигнал MADROE (Memory ADdR Output Enable) - строб, подаваемый на регистры, содержащие адрес, подаваемый на шину RAM

Очень примерно это может выглядеть так (схема без подробностей, только для понимания)

8086-MADROE_и_др.png

И самое главное - диаграмма работы

8086_Gena_TB15_24MHz_timings.png

Тут самое интересное.
CT1 - это такт процессора. Процессору данные нужны по спаду такта. Посему такая логическая разбивка времени.

ReadRAM и WriteRAM - это сигналы, формируемые диспетчером доступа, означают, что происходит с памятью.
RamOE - подаётся на чипы памяти для включения их выхода, активируется только в цикле чтения.
В цикле записи активируется другой сигнал RegOE, формируемый на основе MADROE - включает выходы регистров данных с байтами для записи в память.

По окончании цикла все сигналы с шины памяти снимаются на 20-25 нс, до фронта сигнала MADROE (MASTB) - это обеспечивает гарантию отсутствия перекрытия циклов. Оставшееся время порядка 100 нс вполне достаточно для нормальной работы памяти.

Сам модуль памяти планируется 64-разрядным. Это накладывает некоторые особенности

8086_Memory.png

Процессору 64 разряда не нужны, но нужны для видеоадаптера, чтобы за раз он мог забирать данные для 256-цветных 8 пикселей, иначе ему придётся обращаться к памяти намного чаще.

Слегка чуждый для памяти сигнал CT2 всё-же необходим. С его помощью формируется сигнал WR, который должен подниматься никак не позже окончания цикла и съёма данных и адреса. Данная схема это гарантирует, хотя есть скользкое место, заключающееся в том, что скорость работы ИД14 не бесконечно большая и есть опасения, что могут быть ложные "пички" уже после прихода CT. Хотя в принципе, порядка 15-20 нс на дешифрацию есть.
Как выход из ситуации можно предложить замеc сигнала CT2 с сигналами RAMA0 и BHE - это гарантирует отсутствие "пичек" при любых обстроятельствах, но скорее всего задержит WR. Возможно, что это не критично т.к. по даташиту длительность WR должна быть не менее 50нс, что даёт достаточно времени.

Такой сложный декодер для WR нужен потому что процессор может писать как байтами, так и словами, а память в сумме 64 бита. Поэтому надо выбрать конкретные чипы для записи, а не во все подряд.

А вот чтение с этого модуля всегда происходит на всю ширину. Для процессора далее из 8 регистров, предназначенных для гейта данных RAM-CPU, будут выбраны один или два, с которых чтение и произойдёт. Видеоадаптер же всегда читает 64 бита. DMA - пока вопрос открытый, но по умолчанию работает аналогично процессору, однако задел на 64 бита сделан в виде вывода WR64 - при его активации сигнал записи активируется на всех чипах, независимо от BHE, RAMA0~RAMA2

Память наверное лучше оформить отдельным модулем. Там где-то в районе 400 пинов.

Делитель на 3 можно сделать на D-триггерах (flip-flop) ТМ2. Сигналы получаются такие же.

8086_Gena_TM2_24MHz.png

Здесь нижняя ТМ2 уже относится к селектору, его посмотрим позже. Остальные сигналы такие же. Но не полностью показа формирователь стробов.

У делителей на 3 плохо то, что второй полупериод таковой процессора не получается сделать 50 нс, получается только 41. Понятия не имею, может ли это привести к проблемам. Возможно, нет. Однако унифицирование диаграмм позволит попробовать оба варианта генераторов.

Делитель на 5 даёт точные временнЫе соотношения 75 и 50нс (без учёта фронтов). Его можно сделать на трёх D-триггерах (ТМ2 или ТМ8). Один свободный триггер можно использовать для чего-нибудь ещё, ну или просто для разветвления сигнала, например CT2 - он много где потребуется.

8086_Gena_TM2_40MHz.png

Сигналы те же. Из-за чуть другого количества фронтов временнЫе характеристики слегка другие, но это не принципиально.

То же на ТМ8

8086_Gena_TM8_40MHz.png

На ТМ8 четвёртым триггером можно сформировать сигнал, аналогичный MADROE, но идущий на несколько нс раньше. К сожалению, сделать так, чтобы он начинался чуть ранее MADROE, а заканчивался так же, у меня не получается. Как уже говорил, наверное MADROE будет достаточно, но пока не сделаешь в железе - не узнаешь.

Диаграммы выглядят так

8086_Gena_TM8_40MHz_timings.png

Полагаю, на такой вид диаграмм работы с памятью и рассчитывать в дальнейшем.

Да, про BHE - Byte High Enable. Немного недоумеваю, почему ADR0 не называется BLE - Byte Low Enable. Ну да ладно.
Суть в том, что эти два сигнала ADR0 и BHE индицируют, с какими линиями данных сейчас работает процессор. Лучше всего это отображает табличка

8086_A0_BHE.png

При этом A0, как и BHE как бы не являются адресными сигналами памяти, а только указывают на конкретные байты (младший и/или старший) в слове. Но скорее всего название A0 оставлено для логической совместимости с 8-разрядной адресацией.

Да, даже в Minimum Mode процессор при обращении с данными указывает, какому сегменту (CS, DS, ES, SS) они принадлежат. После сигнала ALE, защёлкивающего адрес  в регистрах, на шину поставляются сигналы S3 и S4, указывающие на конкретный сегмент. Если их использовать, то появляется возможность хоть для каждого сегмента выделить свой собственный мегабайт памяти, что в сумме составит 4 мегабайта максивально возможной памяти, с которой может работать процессор, но на деле такое разбиение неудобно. Однако, если учесть то, что в адресном пространстве всегда есть ПЗУ и видеопамять, мне видится интересным сделать такое разбиение: сегменты CS и DS всегда обращаются к области ПЗУ, где оно перекрывает ОЗУ. А сегменты ES и SS всегда обращаются только к ОЗУ. Это позволит использовать перекрытую ПЗУ память для данных и стека.

Вот табличка

8086_S3_S4_Segment_selector.png

Интресно, ПЗУ сколько по объём надо? 64кБ поди хватит... Накидаем схемку выборки ПЗУ, она будет странная, если не хотим использовать 16-битное ПЗУ (такие тоже есть, ну или две микросхемы можно поставить, но это мне не нравится, одна лучше даже с точки зрения программирования).
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Slabovik

Есть конечно некоторые сомнения. Чисто с эстетической точки зрения очень хочется, чтобы на модуле памяти не было вот этого дешифратора сигналов записи. 120 контактов (треть земляные - это надо, чтобы обеспечить скорость) тоже слегка смущают. Казалось бы, что выход в динамической RAM, но... это уполовинит лишь 20 контактов адреса, что на фоне общего количества будет незаметно, но нужно будет вводить схему мультиплексирования и регенерации, что очень и очень не хочется. Поэтому наверное можно оставить так. Разъём скорее всего типа PBD/PLD. Прямые штыри PLD отлично запаиваются на торец текстолита - расстояние между рядами как раз равно его толщине. Но вставлять будет трудно.

Ладно. Надо посмотреть, что можно сделать с процессором. Пока рассматриваем Minimum Mode, т.е. работу без сопроцессора. В Maximum Mode обвязка несколько сложнее из-за необходимости декодировать больше сигналов.

В очень далёком приближении процессор необходимо обвязать шинами: данных и адреса. Это потому что изначально она у него мультиплексированная и разные данные поставляются на одни и те же выводы, но в разное время.

8086_CPU_BUS.png

Здесь просто обзначены сигналы. Казалось бы, что регистры хранения адреса могли бы работать гейтами, но есть проблема - адресные данные ещё и в других местах используются для дешифрации. Так получается проще. Хотя можно пробовать обойтись дешифрацией во время активного ALE с запоминанием результата. Надо попробовать и такой вариант, но он мне не нравится т.к. ещё есть порты, которым уже самостоятельно придётся делать это.

А так получается, что во время ALE адрес отправляется в регистры и там хранится прям вот до следующего ALE - пользуйтесь.

Когда CPU обращается к памяти, в определённое время (см.ниже) открываются гейты АП5, выдающие на шину памяти адрес.

Вот циклограмма, описывающая две операции: чтение памяти процессором и запись.

8086_CPU&RAM_timings.png

Большая картинка, да. Но не полная, т.к. есть ещё порты (но они работают аналогично), прерывания, освобождение шины. Но это всё потом.

Тут видно, что циклы памяти по длительности равны одному такте процессора, но цикл процессора - это минимум его четыре такта (часто более). К памяти процессор обращается только один раз в цикл.
Всё начинается с выставления сигнала ALE, сопровождаемого выдачей адреса. Также уже сразу известны сигналы Mem/IO и DT/R (Data Translate/Read). Эти два сигнала единственные не меняющиеся в течение цикла.

В центре ALE, по восходящему такту процессора CT1 ловим признак обращения к памяти и вынужденно тащим его при помощи пары триггеров (хочу память и хочу ОЗУ) до начала такта T3, в самом начале которого при помощи схемы обеспечения приоритета (пока не показывал - потом) триггерами выставляем "Цикл CPU-RAM". А в такте T3 получаем данные от RAM, ну или пишем в неё.

Но я обещал схемы выборки ПЗУ. Она отличается от RAM. Во-первых, ПЗУ как правило медленнее. Если RAM обеспечивает 70нс на выдачу данных, по ПЗУ надо 120. Более быстрые ПЗУ есть, но их ещё надо искать.

8086_CPU_BUSRAM_Gates.png

Поскольку по ALE у нас уже известно, что ЦП хочет память, а адрес у нас уже есть, то уже можно начать читать ПЗУ не дожидаясь такта T3.
Адрес на ПЗУ подаётся прямо с процессора (регистров), не через гейт. И благодаря триггеру, первым в течение примерно 4/3 такта читается младшный байт. Затем, уже в такте T2 по подъёму CT1 триггер переключается, чем защёлкивается первый байт в ИР37, младший разряд адреса становится '1' и ПЗУ через какое-то время на выходе даёт уже второй байт. ИР33 в это время прозрачна дляданных, и обе микросхемы по сигналу DEN выдают байты на шину данных процессора. В конце такта T3 он их и заберёт.

Оба байта читаются всегда - это потому что сигналы S3, S4, по которым можно определить, какой сегмент запрашивает данные, ещё не выставлены на шину, а селекция делается позже сигналами OE регистров.

Схемы пока прикидочные, в них могут быть косяки и явные неоптимальности. Но вдруг кто-чего подскажетумное. Например меня интересует вопрос: при чтении процессором одного байта (младшего или старшего) что происходит со второй половиной шины? Вдруг туда можно подавать второй байт? Это упростило бы коммутацию - можно было бы забить на сигналы A0 и BHE в этом случае. Или так нельзя?
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Slabovik

#5
Немного изменил управление памятью. Не нравилось, что CT2 надо заводить на модуль.

8086_RAM_module_schematic.png

В итоге неправильные микросхемы - без инверсии - ЛИ1 да ЛЛ1. Не то, чтобы неправильные, но внутри у них инвертор, поэтому по сравнению с инвертирующими ЛА и ЛЕ они слегка медленнее. Надеюсь, не критично.

Но однако, сигнальных пинов стало 88, что выглядит оптимистично в том плане, что есть типовые 120-пиновые (точнее, два по 60) гребёнки, в т.ч. краевой разъём шины PCI тоже имеет ровно 120 контактов (два ряда по 11+49). Этих разъёмов можно повыпаивать...

Думаю, что модуль памяти можно пробовать разводить. DIP, пожалуй, нафиг, SOIC - да. TSSOP - мелковато.

Немного уточнил генератор на базе делителя на 5. Тема - формирование WR для модуля памяти и сигналов выдачи адреса и данных на шину памяти.  В итоге RAMWR чётко синхронизирован клоком с CT1/CT2, что означает, что сниматься он будет гарантированно раньше съёма с шины данных и адреса. Разница там 5-7-12 наносекунд, но это как раз правильно.

8086_40MHz_gena_TM8_with_strobes.png

Добавленная ТМ8 внизу наверное позволит прямо с неё управлять гейтами. Есть порядка 15-20 нс между выдачей сигнала от диспетчера доступа к памяти, тактируемого CT2 и тактом этой микросхемы. Полагаю, этого должно хватать для отработки комбинаций мелкой логикой. Скорее там надо будет две ТМ8...

Поясняющая диаграмма

8086_40MHz_gena_TM8_diagram.png

Пока вроде нормальненько :)
Надо видео наметить. Там тоже много регистров, но добавятся счётчики. С ними интересно :)

Кстати, да. Регистры для хранеиня-гейтования с ROM можно упростить. По сути, запоминание нужно только в одном из регистров, второй может работать только гейтом, открываясь по OE - микросхема ПЗУ в это время всё-равно выбрана и выдаёт данные. Поэтому этот кусок схемы может выглядеть так

8086_ROM_Reading.png

Кстати, при этом совершенно всё-равно, какой байт читать первым, старший или младший. Главное - запомнить его в нужном регистре.
(правда, чисто идеологически запоминание и синхронное прохождение мне больше нравится, но кому какое дело, если снаружи это всё-равно не видно? Ведь во время этих процессов OE=1...)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Slabovik

#6
Сформировать изображение относительно несложно, кодировать в сигналы телевидения не будем, ибо незачем. Цель - самый простой монитор или SCART-разъём телевизора с раздельными RGB и синхросигналами.
Вывод изображения на монитор или какую другую панель представляет собой построчное сканирование, сопровождаемое выдачей служебных сигналов, по которым определяется начало строки и начало кадра. Минимум - три сигнала. По-хорошему - четыре: видеоизображение, гашение (строчное и кадровое в одном флаконе), синхроимпульсы, строчные и кадровые.

Чисто для понимания, что к чему, строку можно изобразить так

8086_строка_изображения.png

Вроде вопросов нет.
Принято считать, что строка (кадр) начинается не с синхроимпульса, а прямо с момента начала вывода поля изображения, поэтому пронумерованы эти участки именно в таком порядке. На деле же всё-равно, как они нумеруются, главное, чтобы шли друг за другом.

Так вот. Есть большая проблема, если делать длительность всех этих участков жёстко определённой (посредством логики простых микросхем). Обязательно необходима настройка длительности всех четырёх.

Длительность выражается в пикселях для строк, точнее, в 8x пикселей, ибо попиксельный счёт - это чрезмерно быстро. И в строках для кадров. Поскольку кадр развёртывается медленно относительно строк до 1000 и более раз, тут подсчёт каждой строки не вызывает проблем.

Структура строки и кадра, если её изображать, идентична, поэтому картинка применима к обоим.

В видеоконтроллере необходимо иметь узлы:
  • тактовый генератор;
  • узел приёма данных с ОЗУ и их сериализации для вывода (т.е. превращения в пиксели);
  • узел формирователя адреса памяти (откуда данные для пикселей брать);
  • узел счётчиков для перебора режимов "пиксели"-"гашение"-"синхро"-"гашение"-"пиксели" и т.д.;
  • и самое страшное (на самом деле нет) - что-то, хранящее данные для настройки счётчиков перебора режимов.

На деле всё это взаимосвязано, но начнём наверное с тактового. У меня есть непонятки, на чём его лучше сделать. Изначальная схема была такова

8086_Video_gena_ИЕ7.png

Собственно, PixClock получается от какого-нибудь кварцевого генератора (можно PLL внедрить) и являет собой такт для смены пикселей.
Главная частота, выходящая с генератора - 8PixClock. Фактически это одиночный импульс длительностью в период такта PixClock. Но можно сделать и длиннее, правда, LoadNext, который можно наблюдать, должен быть как раз длиной в период такта PixClock - он служит для загрузки очередных 8 пикселей в регистры сдвига. Поэтому 8PixClock удобно иметь такой же.

Но есть проблема. Оказывается, счётчики ИЕ5, ИЕ7 серии 1554 (74AC) формально вроде есть, но... устарели. Большие производители их не делают, предлагают использовать другие. Ну... я слегка переделал гену на ИЕ10, поскольку эти же счётчики решил использовать в качестве  формирователя адреса памяти. Выглядит так

8086_Video_gena_ИЕ10.png

Однако мне не очень это нравится. Если сравнить, то видно, что сигнал 8PixClock в генераторе на ИЕ10 будет слегка запаздывать относительно первой схемы. Поскольку далее по цепочке этим сигналом идёт пересчёт адреса и переключение режимов, это слегка напрягает. Как минимум, сигнал "гашение" будет запаздывать на 15-20 наносекунд (из 100) относительно начала/конца вывода пикселя. Пока понятия не имею, на сколько это может быть критично.

Тем не менее, опираться будем на эти сигналы. Возможно, схему подправим, но сигналы останутся.

Схема сериализации (вывода изображения) выглядит слегка толстенькой, но по сути здесь восемь параллельно работающих регистров

8086_Video_serializator.png

Верхний ряд - регистры, в которые записываются данные из ОЗУ. Нижный ряд - регистры сдвига, каждый выдаёт по одному биту за такт. Через 8 тактов они опустошаются, поэтому каждый 8-й такт происходит их загрузка из верхних регистров, а на схему, которая взаимодействует с памятью, формируется запрос VREQ на чтение следующих 8 пикселей. Всё просто.

Когда активируются сигналы гашения, загрузка пикселей прекращается, но регистры сдвига продолжают работать, выдавая "чёрный", который определяется данными, задвигаемыми с вывода DR. Это может даже сойти за сигнал гашения, кстати...

Циклограмма, но с генератором на ИЕ10 выглядит так

8086_Video_serializator_diagram.png

Ну, как-то так... Далее перебиратор режима  ;)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.