А как наличие общего счетчика может пострадать от перевода с двухфазной модели на однофазную синхронную?
А как наличие общего счетчика может пострадать от перевода с двухфазной модели на однофазную синхронную?
Убедил самого себя
А я бы начал с инициализации сего хитрого девайса. Это очевидно для профи. А чайники потом будут репу чухать. Тем более будет не лишним описать с чего, все стартует. И о блоках(чипа) немного.
Вроде по делу. Простите за мой ломаный английский.
Электроника КР-02, MSX YIS-503IIR, Орион-128, Ленинград-2, Pentagon-128k, MSX2 YIS-503IIIR, MSX-EXT, ...
Сегодня мы разберем системные регистры. Это два регистра, с индексами 0 и 1, которые задают режим работы всего PPU. Собственно, регистры как регистры, сохраняют входящие от CPU данные, синхронизируют их с ядром PPU и выдают их этому ядру. Назначение бит этих регистров следующее:
Регистр #0:
D7 - VBL Разрешение генерации на ноге #19 сигнала низкого уровня после отрисовки растра, которая подключена ко входу NMI CPU (прерывание VBlank).
D6 --- Отсутствует
D5 - O8_16 Высота спрайтов, если 0 то 8 точек, если 1 то 16 точек.
D4 - BGSEL Старший бит адреса знакогенератора для тайлов фона (0ххх/1xxx).
D3 - OBSEL Cтарший бит адреса знакогенератора для спрайтов (0xxx/1xxx).
D2 - I1_32 Приращение адреса при достпуе к VRAM: 0 - 1, 1 - 32 (для рисования горизонтальных или вертикальных линий).
D1, D0 Выбор старшего адреса для таблицы имен тайлов фона (номер экрана), эти биты обрабатываются в формирователе адреса VRAM.
Регистр #1:
D7 - EmB Приглушение синих оттенков, буржуи называют это эмпфазисом, подробнее о нем будет при разборе видео ЦАП.
D6 - EmG Приглушение зеленых оттенков, буржуи называют это эмпфазисом, подробнее о нем будет при разборе видео ЦАП.
D5 - EmR Приглушение красных оттенков, буржуи называют это эмпфазисом, подробнее о нем будет при разборе видео ЦАП.
D4 - OBE Включение отображения спрайтов.
D3 - BGE Включение отображения фона.
D2 - OBCLIP Гашение столбца из левых 8 точек на плоскости спрайтов.
D1 - BGCLIP Гашение столбца из левых 8 точек на плоскости фона.
D0 - B_W Отключение вывода оттенков (Ч/Б вывод изображения).
Оригинальная схема "влоб":
Оптимизированная синхронная схема:
Функциональный аналог Verilog:
Обратите внимание, что я не использую тактовый сигнал PClk (позитивный пиксельклок) как сигнал чувствительности always блока. Промежуточная модель его именно так и использовала, но в строго синхронной модели этого быть не должно. Тактовая частота должна быть одна во всем дизайне. PClk же становится сигналом разрешения, длительностью строго в 1 такт главной тактовой частоты Clk и периодичностью нужные нам 4 такта для NTSC PPU (или 5 тактов для PAL PPU). Эта же тенденция будет сохраняться и для остальных блоков.Код:// Системные регистры 0 / 1 module PPU_SYS_REGS( // Системные входы input Clk, // Тактовая частота input PClk, // Пиксельклок // Управляющие входы input W0, // Запись в регистр 0 input W1, // Запись в регистр 1 input [7:0]DBIN, // Данные // Выходы output reg I1_32, // Увеличение адреса +1/+32 (H/V) output reg OBSEL, // Старший бит адреса знакогенератора спрайтов output reg BGSEL, // Старший бит адреса знакогенератора фона output reg O8_16, // Высота спрайтов (0 - 8 точек, 1 - 16 точек) output reg VBL, // Разрешение прерывания VBlank output reg B_W, // Режим Ч/Б (обнуление младших 4х битов индекса цвета) output reg BGCLIP, // Гашение левого столбца 8 точек у фона output reg OBCLIP, // Гашение левого столбца 8 точек у спрайтов output reg BGE, // Включение фона output reg OBE, // Включение спрайтов output reg EmR, // Эмпфазис красного цвета output reg EmG, // Эмпфазис зеленого цвета output reg EmB // Эмпфазис синего цвета ); // Промежуточные регистры reg [4:0]W0R; reg [7:0]W1R; // Логика always @(posedge Clk) begin if (PClk) begin // Запись значения в промежуточные регистры if (W0) W0R[4:0] <= {DBIN[7],DBIN[5:2]}; if (W1) W1R[7:0] <= DBIN[7:0]; // Обновление значений if (~W0) {VBL,O8_16,BGSEL,OBSEL,I1_32} <= W0R[4:0]; if (~W1) {EmB,EmG,EmR,OBE,BGE,OBCLIP,BGCLIP,B_W} <= W1R[7:0]; end end // Конец endmodule
Последний раз редактировалось HardWareMan; 20.10.2017 в 17:36.
Savepic сдох. Прежде чем перейти к следующему блоку, я попытаюсь восстановить картинки.
Сэйвпик ожил, дублирую фотки себе, в теме буду хостить у себя на всякий случай.
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
Руки дошли, схоронил все картинки. Готовлю следующую тему по мере сил и времени.
Давно я ничего сюда не постил, а время то летит....
Блоки, входными сигналами которых были только внешние сигналы, закончились. Сегодня поговорим о сердце PPU: о синхрогенераторе. Это "сердце" PPU, он дирижирует остальными блоками.
Входные сигналы:
Clk - Главный тактовый сигнал
OBCLIP - Обрезание левой части экрана спрайтов
BGCLIP - Обрезание левой части экрана фона
BGE - Активация фона
OBE - Активация спрайтов
VBL - Разрешение запроса прерывания VBlank
R2 - Чтение регистра 2
nRES - Общий сброс PPU
Выходные сигналы:
PClk - Пиксельклок
ALEGate - Гейт ALE
[5:0]HNN - Синхронизированное атомарное состояние PPU
S_EV - Запуск процесса просмотра списка спрайтов
CLIP_O - Гашение левого столбца из 8ми точек экрана для спрайтов
CLIP_B - Гашение левого столбца из 8ми точек экрана для фона
O_HPOS - Запуск счетчиков координаты X спрайтов (позиция 0 спрайтов)
EVAL - Сброс счетчика OAMT и начало процесса обработки OAMT
E_EV - Инициализация схемы адреса для вычитания графики спрайтов
I_OAM2 - Сигнал очистки OAMT
PAR_O - Вычитывание графики спрайтов
VIS - Маска активного растра (графика выводится на экран)
F_NT - Чтение Name Table
F_TB - Чтение второго байта
F_TA - Чтение первого байта
N_FO - Сигнал разрешения вывода графики фона
F_AT - Чтение атрибутов из Name Table
SC_CNT - Запуск счетчика адресов при включении растра и/или фона
BURST - Маска вывода вспышки синхронизации поднесущей цвета
SYNC - Синхросмесь видеосигнала
PICTURE - Маска выводимого изображения
N_INT - NMI прерывание по VBLANK
R2B7 - Чтение флага NMI
BLNK - Гашение
RESCL - Строка пререндера (сброс всех схем выборки)
[7:0]V - Выход вертикального счетчика (для спрайтовой машины)
HSYNC - Выход строчной синхронизации
VSYNC - Выход кадровой синхронизации
Синхрогенератор содержит 2 счетчика: горизонтальный и вертикальный. Горизонтальный считает пиксели (по пиксельклоку) а вертикальный - строки растра. Каждый сигнал имеет четкую координату Пиксель:Строка, которая в оригинальной схеме задана обычной матрицей:
Я не буду более детально расписывать каждый вырабатываемый сигнал. Это лучше сделать при описании модулей, где конкретный сигнал используется.
По традиции, схема "влоб":
Оптимизированная схема:
Verilog код:
Код:// Главный счетчик таймингов PPU module PPU_HV_COUNTER( // Системные входы input Clk, // Главные такты // Входные цепи input OBCLIP, // Обрезание левой части экрана спрайтов input BGCLIP, // Обрезание левой части экрана фона input BGE, // Активация фона input OBE, // Активация спрайтов input VBL, // Разрешение запроса прерывания VBlank input R2, // Чтение регистра 2 input nRES, // Общий сброс PPU // Выходные цепи output reg PClk, // Пиксельклок output reg ALEGate, // Гейт ALE output reg [5:0]HNN, // Синхронизированное атомарное состояние PPU output reg S_EV, // Запуск процесса просмотра списка спрайтов output reg CLIP_O, // Гашение левого столбца из 8ми точек экрана для спрайтов output reg CLIP_B, // Гашение левого столбца из 8ми точек экрана для фона output reg O_HPOS, // Запуск счетчиков координаты X спрайтов (позиция 0 спрайтов) output reg EVAL, // Сброс счетчика OAMT и начало процесса обработки OAMT output reg E_EV, // Инициализация схемы адреса для вычитания графики спрайтов output reg I_OAM2, // Сигнал очистки OAMT output reg PAR_O, // Вычитывание графики спрайтов output reg VIS, // Маска активного растра (графика выводится на экран) output reg F_NT, // Чтение Name Table output reg F_TB, // Чтение второго байта output reg F_TA, // Чтение первого байта output reg N_FO, // Сигнал разрешения вывода графики фона output reg F_AT, // Чтение атрибутов из Name Table output SC_CNT, // Запуск счетчика адресов при включении растра и/или фона output reg BURST, // Маска вывода вспышки синхронизации поднесущей цвета output reg SYNC, // Синхросмесь видеосигнала output PICTURE, // Маска выводимого изображения output N_INT, // NMI прерывание по VBLANK output reg R2B7, // Чтение флага NMI output BLNK, // Гашение output reg RESCL, // Строка пререндера (сброс всех схем выборки) output [7:0]V, // Выход вертикального счетчика (для спрайтовой машины) output reg HSYNC, // Выход строчной синхронизации output reg VSYNC // Выход кадровой синхронизации ); // Переменные reg [1:0]ClkDiv; // Предделитель тактов reg [8:0]HCnt; // Строчный счетчик reg [8:0]VCnt; // Кадровый счетчик reg V8R; // reg Odd; // Чет/нечет reg VB; // Вертикальное reg FPORCH; reg BPORCH; reg N_HB; reg BREN; reg VSNC; reg PEN; reg [2:0]IFIFO; reg IREQ; reg R2R; reg BLNKR; // Комбинаторика assign SC_CNT = ~N_HB & (BGE | OBE); assign PICTURE = ~PEN & ~BPORCH; assign N_INT = ~(IREQ & VBL); assign BLNK = BLNKR | ~(BGE | OBE); assign V[7:0] = VCnt[7:0]; // Логика always @(posedge Clk) begin // Синхронизация R2R <= R2; V8R <= VCnt[8]; // Запрос прерывания if (~IFIFO[0] & ~IFIFO[2]) IREQ <= 1'b1; else if (R2R | RESCL) IREQ <= 1'b0; // Флаг прерывания if (~R2R & ~R2) R2B7 <= IREQ; // Флаг Odd if (V8R & ~VCnt[8]) Odd <= ~Odd; // Делитель частоты ClkDiv[1:0] <= ClkDiv[1:0] + 2'h1; // Активация пиксельклока строго на 1 такт PClk <= ClkDiv[1] & ClkDiv[0]; // Маска сигнала ALE ALEGate <= ~ClkDiv[1]; // Пиксельклок if (PClk) begin // Синхронизация HNN[5:0] <= HCnt[5:0]; // Счетчик строк if (~nRES | (HCnt[8:0] == 9'd340) | (Odd & (HCnt[8:0] == 9'd339) & RESCL)) begin // Строка досчитала, обрабатываем HCnt[8:0] <= 9'd000; // Счетчик кадров if (~nRES | (VCnt[8:0] == 9'd261)) VCnt[8:0] <= 9'd000; else VCnt[8:0] <= VCnt[8:0] + 9'd001; end else HCnt[8:0] <= HCnt[8:0] + 9'd001; // Сигнал N_FO - Переключение между графикой и служебной информацией N_FO <= ~(BLNK | (HCnt[8] & (~HCnt[6] | HCnt[5] | HCnt[4]))); // Сигнал S_EV S_EV <= ~BLNK & (HCnt[8:0] == 9'd065); // Сигналы отсечения CLIP_O <= ~OBCLIP & (~(HCnt[8] | VB) & ~HCnt[7] & ~HCnt[6] & ~HCnt[5] & ~HCnt[4] & ~HCnt[3]); CLIP_B <= ~BGCLIP & (~(HCnt[8] | VB) & ~HCnt[7] & ~HCnt[6] & ~HCnt[5] & ~HCnt[4] & ~HCnt[3]); // Сигнал позиции O_HPOS <= ~BLNK & (HCnt[8:0] == 9'd339); // Сигнал EVAL EVAL <= ~BLNK & ((HCnt[8:0] == 9'd339) | (HCnt[8:0] == 9'd063) | (HCnt[7:0] == 8'd255)); // Сигнал E_EV E_EV <= ~BLNK & (HCnt[7:0] == 8'd255); // Сигнал I_OAM2 I_OAM2 <= ~BLNK & ~HCnt[8] & ~HCnt[7] & ~HCnt[6]; // Сигнал PAR_O PAR_O <= ~BLNK & HCnt[8] & ~HCnt[7] & ~HCnt[6]; // Сигнал VIS VIS <= ~VB & ~HCnt[8] & ~BLNK; // Сигнал F_NT - Чтение таблицы символов F_NT <= ~BLNK & ~HCnt[2] & ~HCnt[1]; // Сигнал F_TB - Чтение второго байта графики F_TB <= ~(BLNK | (HCnt[8] & (~HCnt[6] | HCnt[5] | HCnt[4]))) & HCnt[2] & HCnt[1]; // Сигнал F_TA - Чтение первого байта графики F_TA <= ~(BLNK | (HCnt[8] & (~HCnt[6] | HCnt[5] | HCnt[4]))) & HCnt[2] & ~HCnt[1]; // Сигнал F_AT - Чтение атрибутов F_AT <= ~(BLNK | (HCnt[8] & (~HCnt[6] | HCnt[5] | HCnt[4]))) & ~HCnt[2] & HCnt[1]; // Сигнал HSYNC HSYNC <= FPORCH; // Сигнал VSYNC VSYNC <= ~VSNC & ~N_HB; // Сигнал SYNC SYNC <= FPORCH | (~VSNC & ~N_HB); // Сигнал BURST BURST <= BREN & (FPORCH | (~VSNC & ~N_HB)); // Сигнал PEN if (VCnt[7:0] == 8'd241) PEN <= 1'b1; else if (VCnt[8:0] == 9'd261) PEN <= 1'b0; // Сигнал BPORCH if (HCnt[8:0] == 9'd270) BPORCH <= 1'b1; else if (HCnt[8:0] == 9'd328) BPORCH <= 1'b0; // Сигнал VB if (VCnt[8:0] == 9'd261) VB <= 1'b0; else if (VCnt[7:0] == 8'd239) VB <= 1'b1; // Сигнал BLNKR if (VCnt[8:0] == 9'd261) BLNKR <= 1'b0; else if (VCnt[7:0] == 8'd240) BLNKR <= 1'b1; // Сигнал FPORCH if (HCnt[8:0] == 9'd256) FPORCH <= 1'b1; else if (HCnt[8:0] == 9'd279) FPORCH <= 1'b0; // Сигнал N_HB if (HCnt[8:0] == 9'd279) N_HB <= 1'b1; else if (HCnt[8:0] == 9'd304) N_HB <= 1'b0; // Сигнал BREN if (HCnt[8:0] == 9'd308) BREN <= 1'b1; else if (HCnt[8:0] == 9'd323) BREN <= 1'b0; // Сигнал VSNC if (N_HB) begin if (VCnt[8:0] == 9'd244) VSNC <= 1'b1; else if (VCnt[8:0] == 9'd247) VSNC <= 1'b0; // FIFO запроса прерывания end IFIFO[2:0] <= {~IFIFO[1],IFIFO[0],~(VCnt[7:0] == 8'd241)}; // Сигнал RESCL RESCL <= (VCnt[8:0] == 9'd261); end end // Конец endmodule
Последний раз редактировалось HardWareMan; 21.10.2017 в 10:40.
Блок формирования управляющих сигналов локальной шины PPU.
Локальная шина PPU это отдельное адресное пространство для памяти, которая хранит текущее состояние картинки. Она состоит из младшего байта адреса и данных, мультиплексированных между собой в сигналы PPU_AD[7..0], старших 6ти бит адреса PPU_A[13..8] и сигналов управления: PPU_ALE для защелкивания адреса на шине PPU_AD[7..0], ~PPU_RD для строба чтения данных и ~PPU_WR для строба записи данных. Направление указано со стороны PPU. Для упрощения, я их буду называть так: PAD[7..0], PA[13..8], ALE, PRD и PWR (на схемах RD и WR соответственно).
- 2 знакогенератора, младшие 8 КБайт: 0000-0FFF и 1000-1FFF.
- 4 т.н. "таблицы имён" (Name Tables, это название появилось из-за тайлового метода формирования изображения PPU: в "таблице имен" заносится код символа из знакогенератора, который потом и отображается на текущем месте тайла, совсем как текст у ВГ75), по 1 КБайту на страницу: 2000-23FF, 2400-27FF, 2800-2BFF и 2C00-2FFF. В каждой странице есть сама страница тайлов x000-x3BF и таблица атрибутов 0x3C0-x3FF.
- 4 палитры фона: 3F00-3F0F
- 4 палитры спрайтов: 3F10-3F1F
Остальные неуказанные области просто не используются. Причем, так как ОЗУ палитры находится внутри PPU то генерация внешних сигналов PRD и PWR не производится, при обращении к области 3F00-3FFF, но область 3000-3EFF программно доступна через регистр #7. Вот схема "влоб":
Сигналы обращения к регистру #7 проходят через синхронизатор и формирователь нужного тайминга для локальной шины PPU. Каждое обращение к регистру #7 формирует строб увеличения счетчика адреса TSTEP, который может влиять на счетчики даже если происходит вывод растра (тот самый конфликт при обращении к VRAM за пределами кадрового гашения). Сигнал DB_PAR сопровождает сигнал записи PWR и сигнализирует о том, что необходим проброс данных с шины CPU D[7..0] на локальную шину PAD[7..0]. Сигнал PD_RB указывает на чтение с шины PAD[7..0] внутрь PPU, и, в зависимости от источника запроса (R7 или синхрогенератор) перенаправляет данные на шину CPU D[7..0] (на это указывает сигнал XRB) или в рабочие регистры или счетчики растра PPU. Сигнал THX_MUX вырабатывается если текущий адрес PA[13..8] равен 3F (обращение к внутреннему ОЗУ палитры) и блокирует внешний сигнал записи PWR а так же сигнал XRB.
Оптимизированная схема:
Здесь появился новый входной сигнал ALEGate, который был введен в рамках организации синхронного дизайна проекта, так как в оригинальной схеме сигнал ALE для режима синхрогенератора (чтение данных растра) организовано как OR сигнала атомарного состояния PPU H0n и инвертированного тактового сигнала PCLK. В синхронной схеме сигнал ALEGate заменяет инвертированный тактовый сигнал PCLK, делая схему строго синхронной. Verilog код модуля:
- - - Добавлено - - -Код:// Управление модулями по адресу module PPU_ADR_CONTROL( // Системные входы input Clk, // Системные такты input PClk, // Такты пикселей input ALEGate, // Формирователь сигнала ALE // Управляющие входы input W7, // Запись в регистр 7 input R7, // Чтение из регистра 7 input H0n, // Слот обращения input BLNK, // Картинка погашена input PA8, // Адрес PA8 input PA9, // Адрес PA9 input PA10, // Адрес PA10 input PA11, // Адрес PA11 input PA12, // Адрес PA12 input PA13, // Адрес PA13 // Выходы output PWR, // Активация записи output TSTEP, // Счет развертки output DB_PAR, // Данные на шину PPU output PD_RB, // Данные в защелку output ALE, // Сигнал ALE output PRD, // Активация чтения output XRB, // Данные на шину CPU output TH_MUX // Обращение в палитру ); // Переменные assign TH_MUX = BLNKR & PA8 & PA9 & PA10 & PA11 & PA12 & PA13; reg [1:0]W7Req; // Очередь запроса записи reg [1:0]W7Queue; // Очередь сигнала записи reg [1:0]R7Req; // Очередь запроса чтения reg [1:0]R7Queue; // Очередь сигнала чтения reg BLNKR; // Регистр гашения // Выход записи assign DB_PAR = W7Queue[1] & ~W7Queue[0]; assign PWR = TH_MUX | ~DB_PAR; // Выход чтения assign PD_RB = R7Queue[1] & ~R7Queue[0]; assign PRD = ~(PD_RB | (H0n & ~BLNKR)); // Выход шага счетчика растра assign TSTEP = DB_PAR | PD_RB; // Выход ALE assign ALE = (~W7Queue[1] & W7Queue[0]) | (~R7Queue[1] & R7Queue[0]) | (ALEGate & ~H0n & ~BLNK); // Данные на шину CPU assign XRB = R7 & ~TH_MUX; // Логика always @(posedge Clk) begin // Синхронизация W7Req[0] <= W7; R7Req[0] <= R7; // Запросы if (W7Req[0] & ~W7) W7Req[1] <= 1'b1; else if (W7Queue[1]) W7Req[1] <= 1'b0; if (R7Req[0] & ~R7) R7Req[1] <= 1'b1; else if (R7Queue[1]) R7Req[1] <= 1'b0; // Пиксельклок if (PClk) begin // Ротация очереди W7Queue[1:0] <= {W7Queue[0],W7Req[1]}; R7Queue[1:0] <= {R7Queue[0],R7Req[1]}; // Синхронизация BLNKR <= BLNK; end end // конец модуля endmodule
Блок мультиплексирования шины данных PPU.
Это маленький блок, который работает при чтении внешним CPU внутреннего состояния PPU или его локальной шины. Схемы не было, он был сразу реализован в виде комбинаторного мультиплексора.
Я же его просто синхронизировал. Объяснять его работу, думаю, не требуется.
Код:// Мультиплексор шины при чтении module PPU_BUS_MUX( // Секция входов input Clk, // Такты input PClk, // Пиксельклок input PD_RB, // Строб моста шины VRAM input XRB, // Выбор чтения VRAM input [7:0]PAD, // Данные VRAM input R2, // Выбор чтения R2 input [7:0]R2DB, // Данные R2 input R4, // Выбор R4 input [7:0]OB, // Данные R4 input RPIX, // Выбор пиксельного вывода input [7:0]PIX, // Данные пиксельного вывода input RnW, // Выбор направления данных input nDBE, // Строб данных // Секция выходов output [7:0]Q // Выход ); // Комбинаторика assign Q[7:0] = (RnW & ~nDBE) ? D[7:0] : 8'hZZ; // Внутренние переменные reg [7:0]ADR; reg [7:0]OBR; reg [7:0]D; // Логика работы мультиплексора always @(posedge Clk) begin // Синхронизация регистра данных обьектов (спрайтов) if (PClk) OBR[7:0] <= OB[7:0]; // Синхронизация данных чтения VRAM if (PD_RB) ADR[7:0] <= PAD[7:0]; // Мультиплексор if (XRB) D[7:0] <= ADR[7:0]; else if (R2) D[7:0] <= R2DB[7:0]; else if (R4) D[7:0] <= OBR[7:0]; else if (RPIX) D[7:0] <= PIX[7:0]; else D[7:0] <= 8'h00; end // Конец модуля endmodule
Последний раз редактировалось HardWareMan; 22.10.2017 в 10:04.
Несколько вопросов к @HardWareMan:
1. Можно восстановить картинки? Все протухли.
2. Еще не сфоткан PAL-овский чип от Dendy 6538 (2C07)?
3. Что изменилось за 2 года?
Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)