diff --git a/.gitignore b/.gitignore index 7d08dc6..ab9ddef 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ blit_goblin blit_goblin.o blit_goblin.s blit_goblin_nubus.raw -blit_goblin_sbus.raw \ No newline at end of file +blit_goblin_sbus.raw +**~ +DeclROM/vid_decl_rom.elf diff --git a/DeclROM/Makefile b/DeclROM/Makefile index cc9751f..f2afbf4 100644 --- a/DeclROM/Makefile +++ b/DeclROM/Makefile @@ -10,17 +10,19 @@ ARCHFLAGS=-march=68020 -mcpu=68020 CFLAGS=-O2 -mpcrel FEATURES= -TARGET=NUBUSFPGA +#TARGET=NUBUSFPGA +# +#ifeq (${TARGET},NUBUSFPGA) +# FEATURES+=-DNUBUSFPGA -DENABLE_RAMDSK -DENABLE_HDMIAUDIO # -DENABLE_SDCARD +#endif +#ifeq (${TARGET},IISIFPGA) +# FEATURES+=-DIISIFPGA -DENABLE_HDMIAUDIO +#endif +#ifeq (${TARGET},QUADRAFPGA) +# FEATURES+=-DQUADRAFPGA -DENABLE_HDMIAUDIO +#endif -ifeq (${TARGET},NUBUSFPGA) - FEATURES+=-DNUBUSFPGA -DENABLE_RAMDSK -DENABLE_HDMIAUDIO # -DENABLE_SDCARD -endif -ifeq (${TARGET},IISIFPGA) - FEATURES+=-DIISIFPGA -DENABLE_HDMIAUDIO -endif -ifeq (${TARGET},QUADRAFPGA) - FEATURES+=-DQUADRAFPGA -DENABLE_HDMIAUDIO -endif +include ../../decl_rom_config.mak CFLAGS+=${FEATURES} HOSTCFLAGS+=${FEATURES} @@ -31,8 +33,8 @@ PROCESS_ROM=${NS816DECLROMDIR}/process_rom APPLEINCS=${NS816DECLROMDIR}/atrap.inc ${NS816DECLROMDIR}/declrom.inc ${NS816DECLROMDIR}/globals.inc -HRES=1920 -VRES=1080 +#HRES=1920 +#VRES=1080 QEMU=no ifeq ($(QEMU),yes) CFLAGS+=-DQEMU diff --git a/DeclROM/NuBusFPGADrvr.h b/DeclROM/NuBusFPGADrvr.h index fa5969f..0a3b8a2 100644 --- a/DeclROM/NuBusFPGADrvr.h +++ b/DeclROM/NuBusFPGADrvr.h @@ -83,15 +83,15 @@ struct MyGammaTbl { }; #define nativeVidMode ((unsigned char)0x80) -/* alternate resolution in 0x81...0x8f */ -#define diskResource ((unsigned char)0x90) +/* alternate resolution in 0x81...0x9f */ +#define diskResource ((unsigned char)0xF0) struct NuBusFPGADriverGlobals { AuxDCEPtr dce; // unused SlotIntQElement *siqel; //unsigned char shadowClut[768]; - unsigned short hres[16]; /* HW max in 0 */ - unsigned short vres[16]; /* HW max in 0 */ + unsigned short hres[32]; /* HW max in 0, WB in even, HW in odd */ + unsigned short vres[32]; /* HW max in 0, WB in even, HW in odd */ unsigned short curPage; unsigned char maxMode; unsigned char curMode; /* mode ; this is resolution (which can't be changed in 7.1 except via reboot ?) */ @@ -123,8 +123,6 @@ static inline unsigned int read_reg(AuxDCEPtr dce, unsigned int reg) { return *((volatile unsigned int*)(dce->dCtlDevBase+GOBOFB_BASE+reg));; } -/* ASM */ -extern SlotIntServiceProcPtr interruptRoutine; /* ctrl */ void linearGamma(NuBusFPGADriverGlobalsPtr dStore) __attribute__ ((section (".text.fbdriver"))); OSErr changeIRQ(AuxDCEPtr dce, char en, OSErr err) __attribute__ ((section (".text.fbdriver"))); diff --git a/DeclROM/NuBusFPGADrvr_Ctrl.c b/DeclROM/NuBusFPGADrvr_Ctrl.c index 281c350..84bccb0 100644 --- a/DeclROM/NuBusFPGADrvr_Ctrl.c +++ b/DeclROM/NuBusFPGADrvr_Ctrl.c @@ -366,6 +366,26 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) return ret; } +#if defined(ENABLE_HDMI_ALT_CHANGE) +#if defined(NUBUSFPGA) +#include "../../nubusfpga_csr_goblin.h" +#include "../../nubusfpga_csr_crg.h" +#elif defined(IISIFPGA) +#include "../../iisifpga_csr_goblin.h" +#include "../../iisifpga_csr_crg.h" +#elif defined(QUADRAFPGA) +#include "../../quadrafpga_csr_crg.h" +#include "../../quadrafpga_csr_goblin.h" +#else +#error "no board defined" +#endif + + + // access to the MMCM +#include "NuBusFPGADrvr_MMCM.h" + +#endif + OSErr reconfHW(AuxDCEPtr dce, unsigned char mode, unsigned char depth, unsigned short page) { NuBusFPGADriverGlobalsHdl dStoreHdl = (NuBusFPGADriverGlobalsHdl)dce->dCtlStorage; NuBusFPGADriverGlobalsPtr dStore = *dStoreHdl; @@ -409,7 +429,11 @@ OSErr reconfHW(AuxDCEPtr dce, unsigned char mode, unsigned char depth, unsigned } SwapMMUMode ( &busMode ); + + write_reg(dce, GOBOFB_VIDEOCTRL, 0); // here we know we're going to change something, disable video */ + if (mode != dStore->curMode) { + UInt8 id = mode - nativeVidMode; unsigned short i; for (i = nativeVidMode ; i <= dStore->maxMode ; i++) { // disable spurious resources, enable only the right one @@ -422,16 +446,126 @@ OSErr reconfHW(AuxDCEPtr dce, unsigned char mode, unsigned char depth, unsigned } dce->dCtlSlotId = mode; // where is that explained ? cscSwitchMode is not in DCDMF3, and you should'nt do that anymore says PDCD... - UInt8 id = mode - nativeVidMode; unsigned int ho = ((dStore->hres[0] - dStore->hres[id]) / 2); unsigned int vo = ((dStore->vres[0] - dStore->vres[id]) / 2); - /* write_reg(dce, GOBOFB_VIDEOCTRL, 0); */ + +#if defined(ENABLE_HDMI_ALT_CHANGE) + // we have the ability to change the hardware resolution + uint32_t a32 = dce->dCtlDevBase; + + // here so that data aren't global + // timings for the VTG +#include "NuBusFPGADrvr_Timings.h" + + const uint8_t reg_addr[12] = { DIV_REG, CLKFBOUT_REG1, CLKFBOUT_REG2, + LOCK_REG1, LOCK_REG2, LOCK_REG3, + FILT_REG1, FILT_REG2, + CLKOUT0_REG1, CLKOUT0_REG2, + CLKOUT1_REG1, CLKOUT1_REG2 }; + const uint16_t reg_mask[12] = { KEEP_IN_DIV, KEEP_IN_MUL_REG1, KEEP_IN_MUL_REG2, + LOCK1_MASK, LOCK23_MASK, LOCK23_MASK, + FILT1_MASK, FILT2_MASK, + REG1_FREQ_MASK & REG1_PHASE_MASK, REG2_FREQ_MASK & REG2_PHASE_MASK, + REG1_FREQ_MASK & REG1_PHASE_MASK, REG2_FREQ_MASK & REG2_PHASE_MASK }; + +#if defined(ENABLE_HDMI_ALT_CHANGE_54MHZ) + const uint16_t freq_25175000[12] = {0x1041, 0x28b, 0x80, 0x1db, 0x7c01, 0x7fe9, 0x9000, 0x100, 0x597, 0x80, 0x105, 0x80}; + const uint16_t freq_40000000[12] = {0x41, 0x493, 0x80, 0xfa, 0x7c01, 0x7fe9, 0x900, 0x1000, 0x30d, 0x80, 0x83, 0x80}; + const uint16_t freq_65000000[12] = {0x1041, 0x249, 0x0, 0x226, 0x7c01, 0x7fe9, 0x9900, 0x1100, 0x1c8, 0x80, 0x42, 0x80}; + const uint16_t freq_108000000[12] = {0x1041, 0x28a, 0x0, 0x1f4, 0x7c01, 0x7fe9, 0x9000, 0x100, 0x145, 0x0, 0x41, 0x0}; + const uint16_t freq_148500000[12] = {0x82, 0x6dc, 0x80, 0xfa, 0x7c01, 0x7fe9, 0x1100, 0x1800, 0x83, 0x80, 0x41, 0x40}; +#elif defined(ENABLE_HDMI_ALT_CHANGE_48MHZ) +#error "Untested input clock for MMCM" + const uint16_t freq_25175000[12] = {0x1041, 0x28b, 0x80, 0x1db, 0x7c01, 0x7fe9, 0x9000, 0x100, 0x514, 0x0, 0x104, 0x0}; + const uint16_t freq_40000000[12] = {0x1041, 0x30d, 0x80, 0x190, 0x7c01, 0x7fe9, 0x1100, 0x9000, 0x3cf, 0x0, 0xc3, 0x0}; + const uint16_t freq_65000000[12] = {0x41, 0x34e, 0x80, 0x15e, 0x7c01, 0x7fe9, 0x900, 0x1000, 0x145, 0x0, 0x41, 0x0}; + const uint16_t freq_108000000[12] = {0x41, 0x597, 0x80, 0xfa, 0x7c01, 0x7fe9, 0x800, 0x8000, 0x145, 0x0, 0x41, 0x0}; + const uint16_t freq_148500000[12] = {0x41, 0x3d0, 0x80, 0x12c, 0x7c01, 0x7fe9, 0x900, 0x1000, 0x83, 0x80, 0x41, 0x40}; +#else +#error "Unknown input clock for MMCM" +#endif + + short timeout = 1000; + while ((read_reg(dce, GOBOFB_VIDEOCTRL) & 0x2 != 0) && timeout) { + /* wait for the reset process to be over */ + /* otherwise without a clock it won't finish */ + timeout --; + delay(100); + } + + if (timeout == 0) + err = ioErr; + // first reset the MCMM and set new values + litex_clk_assert_reg(DRP_RESET); + + const uint16_t* freq = freq_148500000; + const struct vtg_timing_regs *vtgtr = &vtg_1920x1080_60Hz; + + if (id & 0x1) { + switch (dStore->hres[id]) { + case 640: + freq = freq_25175000; + vtgtr = &vtg_640x480_60Hz; + ho = 0; /* full screen, not centered */ + vo = 0; + break; + case 800: + freq = freq_40000000; + vtgtr = &vtg_800x600_60Hz; + ho = 0; + vo = 0; + break; + case 1024: + freq = freq_65000000; + vtgtr = &vtg_1024x768_60Hz; + ho = 0; + vo = 0; + break; + case 1280: + freq = freq_108000000; + vtgtr = &vtg_1280x1024_60Hz; + ho = 0; + vo = 0; + break; + default: + break; + } + } + + for (int i = 0; (i < 12) && (err == noErr); i++) { + err = litex_clk_change_value_norst(a32, + reg_mask[i], freq[i], + reg_addr[i]); + if (err != noErr) goto mmcmdone; + } + + litex_clk_deassert_reg(DRP_RESET); + + err = litex_clk_wait(dce->dCtlDevBase, DRP_LOCKED); + if (err != noErr) goto mmcmdone; + + /* the clock should be back we can finish */ + + // second fully reconfigure the VTG + goblin_video_framebuffer_vtg_hres_write(a32, vtgtr->hres); + goblin_video_framebuffer_vtg_hsync_start_write(a32, vtgtr->hsync_start); + goblin_video_framebuffer_vtg_hsync_end_write(a32, vtgtr->hsync_end); + goblin_video_framebuffer_vtg_hscan_write(a32, vtgtr->hscan); + goblin_video_framebuffer_vtg_vres_write(a32, vtgtr->vres); + goblin_video_framebuffer_vtg_vsync_start_write(a32, vtgtr->vsync_start); + goblin_video_framebuffer_vtg_vsync_end_write(a32, vtgtr->vsync_end); + goblin_video_framebuffer_vtg_vscan_write(a32, vtgtr->vscan); + + mmcmdone: + ; /* nothing */ +#endif + /* center picture in frame */ write_reg(dce, GOBOFB_HRES_START, __builtin_bswap32(ho)); write_reg(dce, GOBOFB_VRES_START, __builtin_bswap32(vo)); write_reg(dce, GOBOFB_HRES_END, __builtin_bswap32(ho + dStore->hres[id])); write_reg(dce, GOBOFB_VRES_END, __builtin_bswap32(vo + dStore->vres[id])); - /* write_reg(dce, GOBOFB_VIDEOCTRL, 1); */ } + if (depth != dStore->curDepth) { switch (depth) { case kDepthMode1: @@ -460,6 +594,16 @@ OSErr reconfHW(AuxDCEPtr dce, unsigned char mode, unsigned char depth, unsigned dStore->curMode = mode; dStore->curDepth = depth; dStore->curPage = page; /* FIXME: HW */ + + short timeout = 1000; + while ((read_reg(dce, GOBOFB_VIDEOCTRL) & 0x2 != 0) && timeout) { + /* wait for the reset process to be over */ + timeout --; + } + if (timeout == 0) + err = ioErr; + + write_reg(dce, GOBOFB_VIDEOCTRL, 1); // restart the video with the new parameters SwapMMUMode ( &busMode ); @@ -506,7 +650,7 @@ OSErr updatePRAM(AuxDCEPtr dce, unsigned char mode, unsigned char depth, unsigne pram.depth = depth; pram.page = page; spb.spSlot = dce->dCtlSlot; - spb.spsPointer = &pram; + spb.spsPointer = (Ptr)&pram; err = SPutPRAMRec(&spb); } return err; diff --git a/DeclROM/NuBusFPGADrvr_MMCM.h b/DeclROM/NuBusFPGADrvr_MMCM.h new file mode 100644 index 0000000..3329fff --- /dev/null +++ b/DeclROM/NuBusFPGADrvr_MMCM.h @@ -0,0 +1,216 @@ + + + +/* MMCM specific numbers */ +#define CLKOUT_MAX 7 +#define DELAY_TIME_MAX 63 +#define PHASE_MUX_MAX 7 +#define HIGH_LOW_TIME_REG_MAX 63 +#define PHASE_MUX_RES_FACTOR 8 + +/* DRP registers index */ +#define DRP_RESET 0 +#define DRP_LOCKED 1 +#define DRP_READ 2 +#define DRP_WRITE 3 +#define DRP_DRDY 4 +#define DRP_ADR 5 +#define DRP_DAT_W 6 +#define DRP_DAT_R 7 + + +/* Register values */ +#define FULL_REG_16 0xFFFF +#define ZERO_REG 0x0 +#define KEEP_IN_MUL_REG1 0xF000 +#define KEEP_IN_MUL_REG2 0xFF3F +#define KEEP_IN_DIV 0xC000 +#define REG1_FREQ_MASK 0xF000 +#define REG2_FREQ_MASK 0x803F +#define REG1_DUTY_MASK 0xF000 +#define REG2_DUTY_MASK 0xFF7F +#define REG1_PHASE_MASK 0x1FFF +#define REG2_PHASE_MASK 0xFCC0 +#define FILT1_MASK 0x66FF +#define FILT2_MASK 0x666F +#define LOCK1_MASK 0xFC00 +#define LOCK23_MASK 0x8000 +/* Control bits extraction masks */ +#define HL_TIME_MASK 0x3F +#define FRAC_MASK 0x7 +#define EDGE_MASK 0x1 +#define NO_CNT_MASK 0x1 +#define FRAC_EN_MASK 0x1 +#define PHASE_MUX_MASK 0x7 + +/* Bit groups start position in DRP registers */ +#define HIGH_TIME_POS 6 +#define LOW_TIME_POS 0 +#define PHASE_MUX_POS 13 +#define FRAC_POS 12 +#define FRAC_EN_POS 11 +#define FRAC_WF_R_POS 10 +#define EDGE_POS 7 +#define NO_CNT_POS 6 +#define EDGE_DIVREG_POS 13 +#define NO_CNT_DIVREG_POS 12 +#define DELAY_TIME_POS 0 + +/* MMCM Register addresses */ +#define POWER_REG 0x28 +#define DIV_REG 0x16 +#define LOCK_REG1 0x18 +#define LOCK_REG2 0x19 +#define LOCK_REG3 0x1A +#define FILT_REG1 0x4E +#define FILT_REG2 0x4F +#define CLKOUT0_REG1 0x08 +#define CLKOUT0_REG2 0x09 +#define CLKOUT1_REG1 0x0A +#define CLKOUT1_REG2 0x0B +#define CLKOUT2_REG1 0x0C +#define CLKOUT2_REG2 0x0D +#define CLKOUT3_REG1 0x0E +#define CLKOUT3_REG2 0x0F +#define CLKOUT4_REG1 0x10 +#define CLKOUT4_REG2 0x11 +#define CLKOUT5_REG1 0x06 +#define CLKOUT5_REG2 0x07 +#define CLKOUT6_REG1 0x12 +#define CLKOUT6_REG2 0x13 +#define CLKFBOUT_REG1 0x14 +#define CLKFBOUT_REG2 0x15 + +#define litex_clk_set_reg(a, b) *((volatile uint32_t*)(a32 + CSR_CRG_VIDEO_PLL_##a##_ADDR + 0)) = __builtin_bswap32(b) +#define litex_clk_get_reg(a) __builtin_bswap32(*((volatile uint32_t*)(a32 + CSR_CRG_VIDEO_PLL_##a##_ADDR))) +#define litex_clk_assert_reg(a) *((volatile uint32_t*)(a32 + CSR_CRG_VIDEO_PLL_##a##_ADDR + 0)) = ((uint32_t)(-1)) +#define litex_clk_deassert_reg(a) *((volatile uint32_t*)(a32 + CSR_CRG_VIDEO_PLL_##a##_ADDR + 0)) = ((uint32_t)(0)) + +#define __ASSERT(a, b) +#define LOG_WRN(a, b) + +static inline void mmcm_waitSome(unsigned long bound) { + unsigned long i; + for (i = 0 ; i < bound ; i++) { + asm volatile("nop"); + } +} +static inline void delay(int d) { + mmcm_waitSome(d * 20); // improveme + return; +} + +static inline int litex_clk_wait(uint32_t a32, uint32_t reg) +{ + uint32_t timeout; + + __ASSERT(reg == DRP_LOCKED || reg == DRP_DRDY, "Unsupported register! Please provide DRP_LOCKED or DRP_DRDY"); + + if (reg == DRP_LOCKED) { + timeout = 1000 /* ldev->timeout.lock */; + while (!litex_clk_get_reg(DRP_LOCKED) && timeout) { + timeout--; + /* k_sleep(K_MSEC(1)); */ + delay(5); + } + } else { + timeout = 1000 /* ldev->timeout.drdy */; + while (!litex_clk_get_reg(DRP_DRDY) && timeout) { + timeout--; + /* k_sleep(K_MSEC(1)); */ + delay(5); + } + } + /*Waiting for signal to assert in reg*/ + /* macro... can't use 'reg' */ + /* while (!litex_clk_get_reg(reg) && timeout) { */ + /* timeout--; */ + /* /\* k_sleep(K_MSEC(1)); *\/ */ + /* delay(5); */ + /* } */ + if (timeout == 0) { + LOG_WRN("Timeout occured when waiting for the register: 0x%x", reg); + return ioErr; + } + return noErr; +} + +/* Read value written in given internal MMCM register*/ +static inline int litex_clk_get_DO(uint32_t a32, uint8_t clk_reg_addr, uint16_t *res) +{ + int ret = noErr; + + litex_clk_set_reg(DRP_ADR, clk_reg_addr); + litex_clk_assert_reg(DRP_READ); + + litex_clk_deassert_reg(DRP_READ); + ret = litex_clk_wait(a32, DRP_DRDY); + if (ret != 0) { + return ret; + } + + *res = litex_clk_get_reg(DRP_DAT_R); + + return noErr; +} + +/* Sets calculated DI value into DI DRP register */ +static inline int litex_clk_set_DI(uint32_t a32, uint16_t DI_val) +{ + int ret = noErr; + + litex_clk_set_reg(DRP_DAT_W, DI_val); + litex_clk_assert_reg(DRP_WRITE); + litex_clk_deassert_reg(DRP_WRITE); + ret = litex_clk_wait(a32, DRP_DRDY); + return ret; +} + +/* Returns raw value ready to be written into MMCM */ +static inline uint16_t litex_clk_calc_DI(uint16_t DO_val, uint16_t mask, + uint16_t bitset) +{ + uint16_t DI_val; + + DI_val = DO_val & mask; + DI_val |= bitset; + + return DI_val; +} + +/* + * Change register value as specified in arguments + * + * mask: preserve or zero MMCM register bits + * by selecting 1 or 0 on desired specific mask positions + * bitset: set those bits in MMCM register which are 1 in bitset + * clk_reg_addr: internal MMCM address of control register + * + */ +static inline int litex_clk_change_value_norst(uint32_t a32, + uint16_t mask, uint16_t bitset, + uint8_t clk_reg_addr) +{ + uint16_t DO_val, DI_val; + int ret = noErr; + + // litex_clk_assert_reg(DRP_RESET); + + ret = litex_clk_get_DO(a32, clk_reg_addr, &DO_val); + if (ret != 0) { + return ret; + } + DI_val = litex_clk_calc_DI(DO_val, mask, bitset); + ret = litex_clk_set_DI(a32, DI_val); + if (ret != 0) { + return ret; + } +#ifdef CONFIG_CLOCK_CONTROL_LOG_LEVEL_DBG + DI_val = litex_clk_get_reg(DRP_DAT_W); + LOG_DBG("set 0x%x under: 0x%x", DI_val, clk_reg_addr); +#endif + litex_clk_deassert_reg(DRP_DAT_W); + // litex_clk_deassert_reg(DRP_RESET); + // ret = litex_clk_wait(DRP_LOCKED); + return ret; +} diff --git a/DeclROM/NuBusFPGADrvr_OpenClose.c b/DeclROM/NuBusFPGADrvr_OpenClose.c index 6ad3db1..463b9f7 100644 --- a/DeclROM/NuBusFPGADrvr_OpenClose.c +++ b/DeclROM/NuBusFPGADrvr_OpenClose.c @@ -21,7 +21,7 @@ __attribute__ ((section (".text.fbdriver"))) short fbIrq(const long sqParameter) ret = 0; irq = (*((volatile unsigned int*)(sqParameter+GOBOFB_BASE+GOBOFB_INTR_CLEAR))); if (irq & 1) { - vblproto myVbl = *(vblproto**)0x0d28; + vblproto myVbl = *(vblproto*)0x0d28; *((volatile unsigned int*)(sqParameter+GOBOFB_BASE+GOBOFB_INTR_CLEAR)) = 0; myVbl((sqParameter>>24)&0xf); // cleaner to use dStore->slot ? but require more code... ret = 1; @@ -88,7 +88,7 @@ OSErr cNuBusFPGAOpen(IOParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) /* ... from ~mac68k, you need option "-mpcrel", and it works */ /* SlotIntServiceProcPtr sqAddr; */ /* asm("lea %%pc@(fbIrq),%0\n" : "=a"(sqAddr)); */ - siqel->sqAddr = fbIrq; + siqel->sqAddr = (void (*)())fbIrq; /* siqel->sqParm = (long)dce; */ siqel->sqParm = (long)dce->dCtlDevBase; dStore->siqel = siqel; @@ -115,7 +115,7 @@ OSErr cNuBusFPGAOpen(IOParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) while ((err == noErr) && (spb.spSlot == dce->dCtlSlot) && (((UInt8)spb.spID) > (UInt8)0x80) && - (((UInt8)spb.spID) < (UInt8)0x90)) { + (((UInt8)spb.spID) < (UInt8)0xA0)) { /* write_reg(dce, GOBOFB_DEBUG, 0xBEEF0020); */ /* write_reg(dce, GOBOFB_DEBUG, spb.spID); */ /* write_reg(dce, GOBOFB_DEBUG, err); */ diff --git a/DeclROM/NuBusFPGADrvr_Status.c b/DeclROM/NuBusFPGADrvr_Status.c index 9a56f1b..f02b056 100644 --- a/DeclROM/NuBusFPGADrvr_Status.c +++ b/DeclROM/NuBusFPGADrvr_Status.c @@ -71,15 +71,15 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) case cscGetPageCnt: /* 4 == cscGetPages */ { VDPageInfo *vPInfo = (VDPageInfo *)*(long *)pb->csParam; - if ((vPInfo->csMode != kDepthMode1) && - (vPInfo->csMode != kDepthMode2) && - (vPInfo->csMode != kDepthMode3) && - (vPInfo->csMode != kDepthMode4) && - (vPInfo->csMode != kDepthMode5) && - (vPInfo->csMode != kDepthMode6)) { - ret = paramErr; - goto done; - } + if ((vPInfo->csMode != kDepthMode1) && + (vPInfo->csMode != kDepthMode2) && + (vPInfo->csMode != kDepthMode3) && + (vPInfo->csMode != kDepthMode4) && + (vPInfo->csMode != kDepthMode5) && + (vPInfo->csMode != kDepthMode6)) { + ret = paramErr; + goto done; + } vPInfo->csPage = (vPInfo->csMode == kDepthMode5) ? 1 : 2; ret = noErr; } @@ -252,25 +252,34 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) } if (((UInt8)vdres->csPreviousDisplayModeID) == dStore->maxMode) vdres->csDisplayModeID = kDisplayModeIDNoMoreResolutions; - else + else { + /* some of the odd numbered modes are missing */ vdres->csDisplayModeID = ((UInt8)vdres->csPreviousDisplayModeID) + 1; - vdres->csHorizontalPixels = dStore->hres[((UInt8)vdres->csDisplayModeID) - nativeVidMode]; - vdres->csVerticalLines = dStore->vres[((UInt8)vdres->csDisplayModeID) - nativeVidMode]; - vdres->csRefreshRate = 60 << 16; /* Fixed(Point) 16+16 */ - vdres->csMaxDepthMode = kDepthMode6; + while ((dStore->hres[((UInt8)vdres->csDisplayModeID) - nativeVidMode] == 0) && + (((UInt8)vdres->csPreviousDisplayModeID) < dStore->maxMode)) + vdres->csDisplayModeID ++; + } + if (vdres->csDisplayModeID >= dStore->maxMode) { + vdres->csDisplayModeID = kDisplayModeIDNoMoreResolutions; + } else { + vdres->csHorizontalPixels = dStore->hres[((UInt8)vdres->csDisplayModeID) - nativeVidMode]; + vdres->csVerticalLines = dStore->vres[((UInt8)vdres->csDisplayModeID) - nativeVidMode]; + vdres->csRefreshRate = (60 << 16) + (vdres->csDisplayModeID & 1); /* Fixed(Point) 16+16 */ + vdres->csMaxDepthMode = kDepthMode6; + } break; case kDisplayModeIDFindFirstResolution: vdres->csDisplayModeID = nativeVidMode; vdres->csHorizontalPixels = dStore->hres[0]; vdres->csVerticalLines = dStore->vres[0]; - vdres->csRefreshRate = 60 << 16; /* Fixed(Point) 16+16 */ + vdres->csRefreshRate = (60 << 16) + (vdres->csDisplayModeID & 1); /* Fixed(Point) 16+16 */ vdres->csMaxDepthMode = kDepthMode6; break; case kDisplayModeIDCurrent: vdres->csDisplayModeID = dStore->curMode; vdres->csHorizontalPixels = dStore->hres[dStore->curMode - nativeVidMode]; vdres->csVerticalLines = dStore->vres[dStore->curMode - nativeVidMode]; - vdres->csRefreshRate = 60 << 16; /* Fixed(Point) 16+16 */ + vdres->csRefreshRate = (60 << 16) + (vdres->csDisplayModeID & 1); /* Fixed(Point) 16+16 */ vdres->csMaxDepthMode = kDepthMode6; break; } diff --git a/DeclROM/NuBusFPGADrvr_Timings.h b/DeclROM/NuBusFPGADrvr_Timings.h new file mode 100644 index 0000000..124ca61 --- /dev/null +++ b/DeclROM/NuBusFPGADrvr_Timings.h @@ -0,0 +1,110 @@ +struct vtg_timing_regs { + unsigned short hres; + unsigned short hsync_start; + unsigned short hsync_end; + unsigned short hscan; + unsigned short vres; + unsigned short vsync_start; + unsigned short vsync_end; + unsigned short vscan; +}; +struct vtg_timing_regs vtg_640x480_60Hz = { + .hres = 640, + .hsync_start = 656, + .hsync_end = 752, + .hscan = 800, + .vres = 480, + .vsync_start = 490, + .vsync_end = 492, + .vscan = 525 +}; +struct vtg_timing_regs vtg_640x480_75Hz = { + .hres = 640, + .hsync_start = 656, + .hsync_end = 720, + .hscan = 840, + .vres = 480, + .vsync_start = 481, + .vsync_end = 484, + .vscan = 500 +}; +struct vtg_timing_regs vtg_800x600_60Hz = { + .hres = 800, + .hsync_start = 840, + .hsync_end = 968, + .hscan = 1056, + .vres = 600, + .vsync_start = 601, + .vsync_end = 605, + .vscan = 628 +}; +struct vtg_timing_regs vtg_800x600_75Hz = { + .hres = 800, + .hsync_start = 816, + .hsync_end = 896, + .hscan = 1056, + .vres = 600, + .vsync_start = 601, + .vsync_end = 604, + .vscan = 625 +}; +struct vtg_timing_regs vtg_1024x768_60Hz = { + .hres = 1024, + .hsync_start = 1048, + .hsync_end = 1184, + .hscan = 1344, + .vres = 768, + .vsync_start = 771, + .vsync_end = 777, + .vscan = 806 +}; +struct vtg_timing_regs vtg_1024x768_75Hz = { + .hres = 1024, + .hsync_start = 1040, + .hsync_end = 1136, + .hscan = 1312, + .vres = 768, + .vsync_start = 769, + .vsync_end = 772, + .vscan = 800 +}; +struct vtg_timing_regs vtg_1280x720_60Hz = { + .hres = 1280, + .hsync_start = 1500, + .hsync_end = 1540, + .hscan = 1650, + .vres = 720, + .vsync_start = 725, + .vsync_end = 730, + .vscan = 750 +}; +struct vtg_timing_regs vtg_1280x1024_60Hz = { + .hres = 1280, + .hsync_start = 1328, + .hsync_end = 1440, + .hscan = 1688, + .vres = 1024, + .vsync_start = 1025, + .vsync_end = 1028, + .vscan = 1066 +}; +struct vtg_timing_regs vtg_1920x1080_30Hz = { + .hres = 1920, + .hsync_start = 2448, + .hsync_end = 2492, + .hscan = 2640, + .vres = 1080, + .vsync_start = 1084, + .vsync_end = 1089, + .vscan = 1125 +}; +struct vtg_timing_regs vtg_1920x1080_60Hz = { + .hres = 1920, + .hsync_start = 2008, + .hsync_end = 2052, + .hscan = 2200, + .vres = 1080, + .vsync_start = 1084, + .vsync_end = 1089, + .vscan = 1125 +}; diff --git a/DeclROM/gen_liteeth_param.sh b/DeclROM/gen_liteeth_param.sh new file mode 100755 index 0000000..72db6ba --- /dev/null +++ b/DeclROM/gen_liteeth_param.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +/bin/rm -f liteeth_param.inc + +touch liteeth_param.inc + +echo -n 'liteethmac_base = ' >> liteeth_param.inc +grep -q csr_base,ethmac ../../csr.csv || echo "0x0" >> liteeth_param.inc +grep csr_base,ethmac ../../csr.csv | awk -F, '{ print $3}' | sed -e 's/0x[fF]0a0/0x00a0/' >> liteeth_param.inc + +echo -n 'liteethphy_base = ' >> liteeth_param.inc +grep -q csr_base,ethphy ../../csr.csv || echo "0x0" >> liteeth_param.inc +grep csr_base,ethphy ../../csr.csv | awk -F, '{ print $3}' | sed -e 's/0x[fF]0a0/0x00a0/' >> liteeth_param.inc + +echo -n 'liteethmac_memory = ' >> liteeth_param.inc +grep -q memory_region,ethmac ../../csr.csv || echo "0x0" >> liteeth_param.inc +grep memory_region,ethmac ../../csr.csv | awk -F, '{ print $3}' | sed -e 's/0x[fF]0c0/0x00c0/' >> liteeth_param.inc diff --git a/DeclROM/gen_mode.c b/DeclROM/gen_mode.c index f12b768..da1c1b6 100644 --- a/DeclROM/gen_mode.c +++ b/DeclROM/gen_mode.c @@ -7,37 +7,38 @@ struct one_res { const unsigned short hres; const unsigned short vres; const unsigned char native_only; + const unsigned char has_hw; }; #define NUM_RES 16 #if 1 static struct one_res res_db[NUM_RES] = { - { 1920, 1080, 0 }, - { 1680, 1050, 0 }, // should be unsuitable - { 1600, 900, 1 }, // freaks out my monitor on 1920x1080, it thinks it's 1680x1050... - { 1440, 900, 0 }, + { 1920, 1080, 0, 0}, /* 148.5 */ + { 1680, 1050, 0, 0}, // should be unsuitable + { 1600, 900, 1, 0}, // freaks out my monitor on 1920x1080, it thinks it's 1680x1050... + { 1440, 900, 0, 0}, - { 1280, 1024, 0 }, - { 1280, 960, 0 }, - { 1280, 800, 0 }, - { 1152, 870, 0 }, + { 1280, 1024, 0, 1}, + { 1280, 960, 0, 0}, + { 1280, 800, 0, 0}, + { 1152, 870, 0, 0}, - { 1152, 864, 0 }, - { 1024, 768, 0 }, - { 832, 624, 0 }, - { 800, 600, 0 }, + { 1152, 864, 0, 0}, + { 1024, 768, 0, 0}, + { 832, 624, 0, 0}, + { 800, 600, 0, 1}, - { 768, 576, 0 }, - { 640, 480, 0 }, - { 512, 384, 0 }, - { 0, 0, 0 } + { 768, 576, 0, 0}, + { 640, 480, 0, 1}, /* 25.125 */ + { 512, 384, 0, 0}, + { 0, 0, 0, 0 } }; #else static struct one_res res_db[NUM_RES] = { - { 1920, 1080, 0 }, - { 1600, 900, 1 }, - /* { 640, 480, 0 }, */ - { 0, 0, 0} + { 1920, 1080, 0, 0 }, + { 1600, 900, 1, 0 }, + /* { 640, 480, 0, 0 }, */ + { 0, 0, 0, 0} }; #endif @@ -59,7 +60,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Resolution: %hu x %hu\n", maxhres, maxvres); id = 0x80; - for (i = 0 ; (res_db[i].hres != 0) && (res_db[i].vres != 0) && (id < 0x90); i++) { // 0x90 is the ram disk + for (i = 0 ; (res_db[i].hres != 0) && (res_db[i].vres != 0) && (id < 0xA0); i++) { // 0xA0 is audio char filename[512]; const unsigned short hres = res_db[i].hres; const unsigned short vres = res_db[i].vres; @@ -81,7 +82,6 @@ int main(int argc, char **argv) { return -1; } enabled[i] = 1; - id ++; for (j = 0 ; j < 6 ; j++) { char modename[128]; @@ -90,7 +90,7 @@ int main(int argc, char **argv) { snprintf(modename, 128, "R%hux%huD%d", hres, vres, depth); fprintf(fd, "\tALIGN 2\n"); - fprintf(fd, "_%sModes: /* id 0x%02x */\n", modename, id-1); + fprintf(fd, "_%sModes: /* id 0x%02x */\n", modename, id); fprintf(fd, "\tOSLstEntry\tmVidParams,_%sParms\t/* offset to vid parameters */\n", modename); fprintf(fd, "\tDatLstEntry\tmPageCnt,%d\t/* number of video pages */\n", (depth == 32) ? 1 : 2); fprintf(fd, "\tDatLstEntry\tmDevType,%s\t/* device type */\n", depth <= 8 ? "clutType" : "directType"); @@ -112,13 +112,47 @@ int main(int argc, char **argv) { fprintf(fd, "\t.long\t0\t/* bmPlaneBytes */\n"); // defmPlaneBytes fprintf(fd, "_End%sParms:\n\n",modename); } + + if (res_db[i].has_hw) { + for (j = 0 ; j < 6 ; j++) { + char modename[128]; + const unsigned short depth = depthdb[j]; + const unsigned short rowBytes = (hres * depth) / 8; + snprintf(modename, 128, "R%hux%huDHW%d", hres, vres, depth); + + fprintf(fd, "\tALIGN 2\n"); + fprintf(fd, "_%sModes: /* id 0x%02x */\n", modename, id + 1); + fprintf(fd, "\tOSLstEntry\tmVidParams,_%sParms\t/* offset to vid parameters */\n", modename); + fprintf(fd, "\tDatLstEntry\tmPageCnt,%d\t/* number of video pages */\n", (depth == 32) ? 1 : 2); + fprintf(fd, "\tDatLstEntry\tmDevType,%s\t/* device type */\n", depth <= 8 ? "clutType" : "directType"); + fprintf(fd, "\t.long\tEndOfList\t/* end of list */\n"); + fprintf(fd, "_%sParms:\n", modename); + fprintf(fd, "\t.long\t_End%sParms-_%sParms\t/* physical block size */\n", modename, modename); + fprintf(fd, "\t.long\t0\t/* QuickDraw base offset ; vpBaseOffset */\n"); // defmBaseOffset + fprintf(fd, "\t.word\t%hu\t/* physRowBytes ; vpRowBytes */\n", rowBytes); + fprintf(fd, "\t.word\t0,0,%hu,%hu\t/* vpBounds */\n", vres, hres); + fprintf(fd, "\t.word\tdefVersion\t/* bmVersion ; vpVersion */\n"); + fprintf(fd, "\t.word\t0\t/* packType not used ; vpPackType */\n"); + fprintf(fd, "\t.long\t0\t/* packSize not used ; vpPackSize */\n"); + fprintf(fd, "\t.long\tdefmHRes\t/* bmHRes */\n"); + fprintf(fd, "\t.long\tdefmVRes\t/* bmVRes */\n"); + fprintf(fd, "\t.word\t%s\t/* bmPixelType */\n", depth <= 8 ? "ChunkyIndexed" : "ChunkyDirect"); + fprintf(fd, "\t.word\t%d\t/* bmPixelSize */\n", depth); + fprintf(fd, "\t.word\t%d\t/* bmCmpCount */\n", depth <= 8 ? 1 : 3); + fprintf(fd, "\t.word\t%d\t/* bmCmpSize */\n", depth <= 8 ? depth : (depth == 32 ? 8 : 5)); + fprintf(fd, "\t.long\t0\t/* bmPlaneBytes */\n"); // defmPlaneBytes + fprintf(fd, "_End%sParms:\n\n",modename); + } + } + fclose(fd); + + id += 2 ; // even are 'windowboxed' // here we are 'enabled' } { char filename[512]; FILE *fd; - id = 0x80; snprintf(filename, 512, "VidRomRes.s"); fd = fopen(filename, "w"); if (fd == NULL) { @@ -167,10 +201,37 @@ int main(int argc, char **argv) { fprintf(fd, "_ScreenNameGoboFB_R%hux%hu:\n", hres, vres); fprintf(fd, "\t.long\t_ScreenNameGoboFB_R%hux%huEnd - _ScreenNameGoboFB_R%hux%hu\n", hres, vres, hres, vres); fprintf(fd, "\t.word\t0\n"); - fprintf(fd, "\t.string\t\"GoblinFB %hux%hu%s\\0\"\n", hres, vres, native ? " (N)": ""); + fprintf(fd, "\t.string\t\"GoblinFB %hux%hu%s\\0\"\n", hres, vres, native ? " (N)" : ""); fprintf(fd, "_ScreenNameGoboFB_R%hux%huEnd:\n", hres, vres); } } + if (res_db[i].has_hw) { + fprintf(fd, "\tALIGN 2\n"); + fprintf(fd, "_VModeName:\n"); + + for (i = 0 ; (res_db[i].hres != 0) && (res_db[i].vres != 0) ; i++) { + const unsigned short hres = res_db[i].hres; + const unsigned short vres = res_db[i].vres; + if (enabled[i]) { + fprintf(fd, "\tOSLstEntry\tsRsrc_GoboFB_R%hux%huHW,_ScreenNameGoboFB_R%hux%huHW\n", hres, vres, hres, vres); + } + } + fprintf(fd, "\tDatLstEntry endOfList, 0\n"); + + for (i = 0 ; (res_db[i].hres != 0) && (res_db[i].vres != 0) ; i++) { + const unsigned short hres = res_db[i].hres; + const unsigned short vres = res_db[i].vres; + if (enabled[i]) { + int native = (hres == maxhres) && (vres == maxvres); + fprintf(fd, "\tALIGN 2\n"); + fprintf(fd, "_ScreenNameGoboFB_R%hux%huHW:\n", hres, vres); + fprintf(fd, "\t.long\t_ScreenNameGoboFB_R%hux%huHWEnd - _ScreenNameGoboFB_R%hux%huHW\n", hres, vres, hres, vres); + fprintf(fd, "\t.word\t0\n"); + fprintf(fd, "\t.string\t\"GoblinFB %hux%hu%s\\0\"\n", hres, vres, " HW"); + fprintf(fd, "_ScreenNameGoboFB_R%hux%huHWEnd:\n", hres, vres); + } + } + } fclose(fd); } @@ -207,6 +268,26 @@ int main(int argc, char **argv) { fprintf(fd, "\tOSLstEntry\tfifthVidMode,_R%hux%huD32Modes\t/* offset to 24/32 Bit Mode parms */\n", hres, vres); fprintf(fd, "\tOSLstEntry\tsixthVidMode,_R%hux%huD16Modes\t/* offset to 15/16 Bit Mode parms */\n", hres, vres); fprintf(fd, "\t.long EndOfList\t/* end of list */\n\n"); + if (res_db[i].has_hw) { + fprintf(fd, "\tALIGN 2\n"); + fprintf(fd, "_sRsrc_GoboFB_R%hux%huHW:\n", hres, vres); + fprintf(fd, "\tOSLstEntry\tsRsrcType,_GoboFBType\t/* video type descriptor */\n"); + fprintf(fd, "\tOSLstEntry\tsRsrcName,_GoboFBName\t/* offset to driver name string */\n"); + fprintf(fd, "\tOSLstEntry\tsRsrcDrvrDir,_GoboFBDrvrDir /* offset to driver directory */\n"); + fprintf(fd, "\tDatLstEntry\tsRsrcFlags,%d\t/* force 32 bits mode & open (native) - or not (others)*/\n", native ? 6 : 0); + fprintf(fd, "\tDatLstEntry\tsRsrcHWDevId,1\t/* hardware device ID */\n"); + fprintf(fd, "\tOSLstEntry\tMinorBaseOS,_MinorBase\t/* offset to frame buffer array */\n"); + fprintf(fd, "\tOSLstEntry\tMinorLength,_MinorLength\t/* offset to frame buffer length */\n"); + fprintf(fd, "\t/* OSLstEntry\tsGammaDir,_GammaDirS\t/* directory for 640x480 monitor */\n"); + fprintf(fd, "/* Parameters */\n"); + fprintf(fd, "\tOSLstEntry\tfirstVidMode,_R%hux%huDHW8Modes\t/* offset to 8 Bit Mode parms */\n", hres, vres); + fprintf(fd, "\tOSLstEntry\tsecondVidMode,_R%hux%huDHW4Modes\t/* offset to 4 Bit Mode parms */\n", hres, vres); + fprintf(fd, "\tOSLstEntry\tthirdVidMode,_R%hux%huDHW2Modes\t/* offset to 2 Bit Mode parms */\n", hres, vres); + fprintf(fd, "\tOSLstEntry\tfourthVidMode,_R%hux%huDHW1Modes\t/* offset to 1 Bit Mode parms */\n", hres, vres); + fprintf(fd, "\tOSLstEntry\tfifthVidMode,_R%hux%huDHW32Modes\t/* offset to 24/32 Bit Mode parms */\n", hres, vres); + fprintf(fd, "\tOSLstEntry\tsixthVidMode,_R%hux%huDHW16Modes\t/* offset to 15/16 Bit Mode parms */\n", hres, vres); + fprintf(fd, "\t.long EndOfList\t/* end of list */\n\n"); + } } } fclose(fd); @@ -215,7 +296,6 @@ int main(int argc, char **argv) { { char filename[512]; FILE *fd; - unsigned char id = 0x80; snprintf(filename, 512, "VidRomDir.s"); fd = fopen(filename, "w"); if (fd == NULL) { @@ -244,7 +324,10 @@ int main(int argc, char **argv) { const unsigned short hres = res_db[i].hres; const unsigned short vres = res_db[i].vres; if (enabled[i]) { - fprintf(fd, "sRsrc_GoboFB_R%hux%hu = 0x%02hhx\n", hres, vres, id++); + fprintf(fd, "sRsrc_GoboFB_R%hux%hu = 0x%02hhx\n", hres, vres, id); + if (res_db[i].has_hw) + fprintf(fd, "sRsrc_GoboFB_R%hux%huHW = 0x%02hhx\n", hres, vres, id+1); + id += 2; } } @@ -267,6 +350,9 @@ int main(int argc, char **argv) { const unsigned short vres = res_db[i].vres; if (enabled[i]) { fprintf(fd, "\tOSLstEntry\tsRsrc_GoboFB_R%hux%hu,_sRsrc_GoboFB_R%hux%hu/* video sRsrc List */\n", hres, vres, hres, vres); + if (res_db[i].has_hw) { + fprintf(fd, "\tOSLstEntry\tsRsrc_GoboFB_R%hux%huHW,_sRsrc_GoboFB_R%hux%huHW/* video sRsrc List */\n", hres, vres, hres, vres); + } } } #ifdef ENABLE_RAMDSK diff --git a/DeclROM/vid_decl_rom.s b/DeclROM/vid_decl_rom.s index 97626cd..2c4ec6e 100644 --- a/DeclROM/vid_decl_rom.s +++ b/DeclROM/vid_decl_rom.s @@ -8,8 +8,8 @@ sRsrc_Board = 1 /* board sResource (>0 & <128) */ .include "VidRomDef.s" -sRsrc_RAMDsk = 0x90 /* functional sResources */ -sRsrc_SDCard = 0x91 /* functional sResources */ +sRsrc_RAMDsk = 0xF0 /* functional sResources */ +sRsrc_SDCard = 0xF1 /* functional sResources */ sRsrc_HDMIAudio = 0xA0 /* functional sResources */ .global DeclROMDir diff --git a/goblin_alt_fb.py b/goblin_alt_fb.py index a2c4de1..6a585db 100644 --- a/goblin_alt_fb.py +++ b/goblin_alt_fb.py @@ -48,8 +48,6 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR): print(f"FRAMEBUFFER: dram_port.data_width = {dram_port.data_width}, {hres}x{vres}, 0x{base:x}, in {clock_domain}, clock_faster_than_sys={clock_faster_than_sys}") vga_sync = getattr(self.sync, clock_domain) # usually should be named hdmi_sync, really... - - npixels = hres * vres # default to max # if 0, 32-bits mode # should only be changed while in reset @@ -150,7 +148,7 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR): self.submodules.fb_dma = LiteDRAMFBDMAReader(dram_port, fifo_depth = fifo_depth//(dram_port.data_width//8), default_base = base, - default_length = npixels) + default_length = (hres * vres)) # default to max # If DRAM Data Width > 8-bit and Video clock is faster than sys_clk: # actually always use that case to simplify the design @@ -393,7 +391,7 @@ class GoblinAlt(Module, AutoCSR): name = "video_framebuffer" # near duplicate of plaform.add_video_framebuffer - # Video Timing Generator. Interface, we only keep the CSR & timingd stuff for compatibility + # Video Timing Generator. Interface, we only keep the CSR & timings stuff for compatibility vtg = FBVideoTimingGeneratorInterface(default_video_timings=timings if isinstance(timings, str) else timings[1]) vtg = ClockDomainsRenamer(clock_domain)(vtg) setattr(self.submodules, f"{name}_vtg", vtg) @@ -455,6 +453,7 @@ class GoblinAlt(Module, AutoCSR): bt_addr = Signal(8, reset = 0) # reg 0x14 ; lut itself in reg 0x18 bt_cmap_state = Signal(2, reset = 0) m_vbl_disable = Signal(reset = 1) # reg 0x4 + bt_upd = Signal() # for sub-resolution hres_start = Signal(hbits, reset = 0) @@ -464,6 +463,8 @@ class GoblinAlt(Module, AutoCSR): vres_upd = Signal() videoctrl = Signal() # reg 0x8 + videoctrl_upd = Signal() + videoctrl_busy = Signal() vbl_signal = Signal(reset = 0) # reg 0xC self.comb += irq_line.eq(~vbl_signal | m_vbl_disable) # irq_line is active low @@ -485,11 +486,13 @@ class GoblinAlt(Module, AutoCSR): Case(bus.adr[0:18], { "default": [], # gobofb_mode - 0x0: [ NextValue(bt_mode, bus.dat_w[low_byte]), ], + 0x0: [ NextValue(bt_mode, bus.dat_w[low_byte]), + NextValue(bt_upd, 1), ], # set vbl 0x1: [ NextValue(m_vbl_disable, ~bus.dat_w[low_bit]), ], # gobofb on/off - 0x2: [ NextValue(videoctrl, bus.dat_w[low_bit]), ], + 0x2: [ NextValue(videoctrl, bus.dat_w[low_bit]), + NextValue(videoctrl_upd, 1), ], # clear irq 0x3: [ NextValue(vbl_signal, 0), ], # 0x4: reset in SW @@ -548,7 +551,7 @@ class GoblinAlt(Module, AutoCSR): Case(bus.adr[0:18], { # bt_addr 0x0: [ NextValue(bus.dat_r[low_byte], bt_mode), ], - 0x2: [ NextValue(bus.dat_r[low_byte], videoctrl), ], + 0x2: [ NextValue(bus.dat_r[low_byte], Cat(videoctrl, videoctrl_busy)), ], 0x3: [ NextValue(bus.dat_r[low_byte], ~irq_line), ], # irq_line is active low "default": [ NextValue(bus.dat_r, 0xDEADBEEF)], 0x10: [ NextValue(bus.dat_r, hres), ], # hres (r/o) # FIXME: endianess @@ -565,8 +568,7 @@ class GoblinAlt(Module, AutoCSR): ) # mode switch logic #npixels = hres * vres - npixels = Signal(hbits + vbits +1, reset = (hres * vres)) - old_bt_mode = Signal(8) # different from bt_mode + npixels = Signal(hbits + vbits + 1, reset = (hres * vres)) in_reset = Signal() post_reset_ctr = Signal(3) previous_videoctrl = Signal() @@ -582,16 +584,18 @@ class GoblinAlt(Module, AutoCSR): handle_truecolor_bit = [ self.video_framebuffer.use_indexed.eq(~bt_mode[4:5]) ] else: handle_truecolor_bit = [ ] - + # this has grown complicated and should be a FSM... - self.sync += [ old_bt_mode.eq(bt_mode), - If((old_bt_mode != bt_mode) | vres_upd, + self.sync += [ If(bt_upd | vres_upd, + bt_upd.eq(0), vres_upd.eq(0), in_reset.eq(1), + post_reset_ctr.eq(0), videoctrl.eq(0), # start a disabling cycle, or stay disabled - previous_videoctrl.eq(videoctrl), # preserve old state for restoration later + videoctrl_upd.eq(1), # start a disabling cycle, or stay disabled + previous_videoctrl.eq(videoctrl | previous_videoctrl), # preserve old state for restoration later, if 'previous' is already set then we just had an update in the middle of an update... ), - If(in_reset & ~vtg_enable, # we asked for a reset and by now, the VTG has been turned off (or was off) + If(~bt_upd & ~vres_upd & in_reset & ~vtg_enable, # we asked for a reset and by now, the VTG has been turned off (or was off) self.video_framebuffer.indexed_mode.eq(bt_mode[0:2]), *handle_truecolor_bit, in_reset.eq(0), @@ -602,7 +606,7 @@ class GoblinAlt(Module, AutoCSR): vtg._vres_start.eq(vres_start), vtg._vres_end.eq( vres_end), ), - If(post_reset_ctr == 4, # now reconfigure the DMA + If(~bt_upd & ~vres_upd & (post_reset_ctr == 4), # now reconfigure the DMA If(bt_mode[4:5], Case(bt_mode[0:2], { 0x0: self.video_framebuffer.fb_dma.length.eq(npixels << 2), @@ -617,11 +621,13 @@ class GoblinAlt(Module, AutoCSR): }), ), ), - If(post_reset_ctr == 1, # we've waited for the mode switch so restore video mode - videoctrl.eq(previous_videoctrl), + If(~bt_upd & ~vres_upd & (post_reset_ctr == 1) & previous_videoctrl, # we've waited for the mode switch so restore video ctrl if set + videoctrl.eq(1), + videoctrl_upd.eq(1), + previous_videoctrl.eq(0), # reset, ow that the update is finished ), - If(post_reset_ctr != 0, - post_reset_ctr.eq(post_reset_ctr - 1), + If(~bt_upd & ~vres_upd & (post_reset_ctr != 0), + post_reset_ctr.eq(post_reset_ctr - 1), ), ] @@ -630,29 +636,40 @@ class GoblinAlt(Module, AutoCSR): videoctrl_starting = Signal() videoctrl_stopping = Signal() self.sync += [ - If(~videoctrl_starting & ~videoctrl_stopping, # while we're changing state, delay any new request for change - old_videoctrl.eq(videoctrl), - ), # turn on - If(videoctrl & ~old_videoctrl, # pos edge - self.video_framebuffer.fb_dma.enable.eq(1), # enable DMA - videoctrl_starting.eq(1), + If(videoctrl & videoctrl_upd & ~videoctrl_starting & ~videoctrl_stopping, + If(~old_videoctrl, + self.video_framebuffer.fb_dma.enable.eq(1), # enable DMA + videoctrl_starting.eq(1), + videoctrl_upd.eq(0), + ).Else( # already on, ignore? + videoctrl_upd.eq(0), + ) ), - If(videoctrl & (self.video_framebuffer.fb_dma.rsv_level != 0), + If(videoctrl_starting & (self.video_framebuffer.fb_dma.rsv_level != 0), vtg_enable.eq(1), # there's some data requested, good to go - videoctrl_starting.eq(0), + old_videoctrl.eq(1), # we're on + videoctrl_starting.eq(0), # starting finished ), # turn off - If(~videoctrl & old_videoctrl, # neg edge - self.video_framebuffer.fb_dma.enable.eq(0), # disable DMA - videoctrl_stopping.eq(1), + If(~videoctrl & videoctrl_upd & ~videoctrl_starting & ~videoctrl_stopping, # neg edge + If(old_videoctrl, + self.video_framebuffer.fb_dma.enable.eq(0), # disable DMA + videoctrl_stopping.eq(1), + videoctrl_upd.eq(0), + ).Else( # already off, ignore? + videoctrl_upd.eq(0), + ) ), - If(~videoctrl & (self.video_framebuffer.fb_dma.rsv_level == 0) & (self.video_framebuffer.underflow), + If(videoctrl_stopping & (self.video_framebuffer.fb_dma.rsv_level == 0) & (self.video_framebuffer.underflow), vtg_enable.eq(0), # the DMA FIFO is purged, stop vtg + old_videoctrl.eq(0), videoctrl_stopping.eq(0), ), - ] - + ] + + self.comb += [ videoctrl_busy.eq(videoctrl_starting | videoctrl_stopping | in_reset | (post_reset_ctr != 0) | videoctrl_upd) ] + # VBL logic self.sync += [ If(self.video_framebuffer.vblping == 1, diff --git a/goblin_fb.py b/goblin_fb.py index 8b11390..6dc520f 100644 --- a/goblin_fb.py +++ b/goblin_fb.py @@ -45,9 +45,7 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR): print(f"FRAMEBUFFER: dram_port.data_width = {dram_port.data_width}, {hres}x{vres}, 0x{base:x}, in {clock_domain}, clock_faster_than_sys={clock_faster_than_sys}") - vga_sync = getattr(self.sync, clock_domain) - - npixels = hres * vres # default to max + vga_sync = getattr(self.sync, clock_domain) # usually should be named hdmi_sync, really... # if 0, 32-bits mode # should only be changed while in reset @@ -55,6 +53,7 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR): # mode, as x in 2^x (so 1, 2, 4, 8 bits) # should only be changed while in reset self.indexed_mode = Signal(2, reset = 0x3) + # for the VBL interrupt self.vblping = Signal(reset = 0) if (hwcursor): @@ -146,7 +145,7 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR): self.submodules.fb_dma = LiteDRAMFBDMAReader(dram_port, fifo_depth = fifo_depth//(dram_port.data_width//8), default_base = base, - default_length = npixels) + default_length = (hres * vres)) # default to max # If DRAM Data Width > 8-bit and Video clock is faster than sys_clk: # actually always use that case to simplify the design @@ -463,6 +462,7 @@ class Goblin(Module, AutoCSR): bt_addr = Signal(8, reset = 0) # reg 0x14 ; lut itself in reg 0x18 bt_cmap_state = Signal(2, reset = 0) m_vbl_disable = Signal(reset = 1) # reg 0x4 + bt_upd = Signal() # for sub-resolution hres_start = Signal(hbits, reset = 0) @@ -472,6 +472,8 @@ class Goblin(Module, AutoCSR): vres_upd = Signal() videoctrl = Signal() # reg 0x8 + videoctrl_upd = Signal() + videoctrl_busy = Signal() vbl_signal = Signal(reset = 0) # reg 0xC self.comb += irq_line.eq(~vbl_signal | m_vbl_disable) # irq_line is active low @@ -493,11 +495,13 @@ class Goblin(Module, AutoCSR): Case(bus.adr[0:18], { "default": [], # gobofb_mode - 0x0: [ NextValue(bt_mode, bus.dat_w[low_byte]), ], + 0x0: [ NextValue(bt_mode, bus.dat_w[low_byte]), + NextValue(bt_upd, 1), ], # set vbl 0x1: [ NextValue(m_vbl_disable, ~bus.dat_w[low_bit]), ], # gobofb on/off - 0x2: [ NextValue(videoctrl, bus.dat_w[low_bit]), ], + 0x2: [ NextValue(videoctrl, bus.dat_w[low_bit]), + NextValue(videoctrl_upd, 1), ], # clear irq 0x3: [ NextValue(vbl_signal, 0), ], # 0x4: reset in SW @@ -556,7 +560,7 @@ class Goblin(Module, AutoCSR): Case(bus.adr[0:18], { # bt_addr 0x0: [ NextValue(bus.dat_r[low_byte], bt_mode), ], - 0x2: [ NextValue(bus.dat_r[low_byte], videoctrl), ], + 0x2: [ NextValue(bus.dat_r[low_byte], Cat(videoctrl, videoctrl_busy)), ], 0x3: [ NextValue(bus.dat_r[low_byte], ~irq_line), ], # irq_line is active low "default": [ NextValue(bus.dat_r, 0xDEADBEEF)], 0x10: [ NextValue(bus.dat_r, hres), ], # hres (r/o) # FIXME: endianess @@ -573,8 +577,7 @@ class Goblin(Module, AutoCSR): ) # mode switch logic #npixels = hres * vres - npixels = Signal(hbits + vbits +1, reset = (hres * vres)) - old_bt_mode = Signal(8) # different from bt_mode + npixels = Signal(hbits + vbits + 1, reset = (hres * vres)) in_reset = Signal() post_reset_ctr = Signal(3) previous_videoctrl = Signal() @@ -590,16 +593,18 @@ class Goblin(Module, AutoCSR): handle_truecolor_bit = [ self.video_framebuffer.use_indexed.eq(~bt_mode[4:5]) ] else: handle_truecolor_bit = [ ] - + # this has grown complicated and should be a FSM... - self.sync += [ old_bt_mode.eq(bt_mode), - If((old_bt_mode != bt_mode) | vres_upd, + self.sync += [ If(bt_upd | vres_upd, + bt_upd.eq(0), vres_upd.eq(0), in_reset.eq(1), + post_reset_ctr.eq(0), videoctrl.eq(0), # start a disabling cycle, or stay disabled - previous_videoctrl.eq(videoctrl), # preserve old state for restoration later + videoctrl_upd.eq(1), # start a disabling cycle, or stay disabled + previous_videoctrl.eq(videoctrl | previous_videoctrl), # preserve old state for restoration later, if 'previous' is already set then we just had an update in the middle of an update... ), - If(in_reset & ~vtg_enable, # we asked for a reset and by now, the VTG has been turned off (or was off) + If(~bt_upd & ~vres_upd & in_reset & ~vtg_enable, # we asked for a reset and by now, the VTG has been turned off (or was off) self.video_framebuffer.indexed_mode.eq(bt_mode[0:2]), *handle_truecolor_bit, in_reset.eq(0), @@ -610,7 +615,7 @@ class Goblin(Module, AutoCSR): vtg._vres_start.eq(vres_start), vtg._vres_end.eq( vres_end), ), - If(post_reset_ctr == 4, # now reconfigure the DMA + If(~bt_upd & ~vres_upd & (post_reset_ctr == 4), # now reconfigure the DMA If(bt_mode[4:5], Case(bt_mode[0:2], { 0x0: self.video_framebuffer.fb_dma.length.eq(npixels << 2), @@ -625,11 +630,13 @@ class Goblin(Module, AutoCSR): }), ), ), - If(post_reset_ctr == 1, # we've waited for the mode switch so restore video mode - videoctrl.eq(previous_videoctrl), + If(~bt_upd & ~vres_upd & (post_reset_ctr == 1) & previous_videoctrl, # we've waited for the mode switch so restore video ctrl if set + videoctrl.eq(1), + videoctrl_upd.eq(1), + previous_videoctrl.eq(0), # reset, ow that the update is finished ), - If(post_reset_ctr != 0, - post_reset_ctr.eq(post_reset_ctr - 1), + If(~bt_upd & ~vres_upd & (post_reset_ctr != 0), + post_reset_ctr.eq(post_reset_ctr - 1), ), ] @@ -638,29 +645,40 @@ class Goblin(Module, AutoCSR): videoctrl_starting = Signal() videoctrl_stopping = Signal() self.sync += [ - If(~videoctrl_starting & ~videoctrl_stopping, # while we're changing state, delay any new request for change - old_videoctrl.eq(videoctrl), - ), # turn on - If(videoctrl & ~old_videoctrl, # pos edge - self.video_framebuffer.fb_dma.enable.eq(1), # enable DMA - videoctrl_starting.eq(1), + If(videoctrl & videoctrl_upd & ~videoctrl_starting & ~videoctrl_stopping, + If(~old_videoctrl, + self.video_framebuffer.fb_dma.enable.eq(1), # enable DMA + videoctrl_starting.eq(1), + videoctrl_upd.eq(0), + ).Else( # already on, ignore? + videoctrl_upd.eq(0), + ) ), - If(videoctrl & (self.video_framebuffer.fb_dma.rsv_level != 0), + If(videoctrl_starting & (self.video_framebuffer.fb_dma.rsv_level != 0), vtg_enable.eq(1), # there's some data requested, good to go - videoctrl_starting.eq(0), + old_videoctrl.eq(1), # we're on + videoctrl_starting.eq(0), # starting finished ), # turn off - If(~videoctrl & old_videoctrl, # neg edge - self.video_framebuffer.fb_dma.enable.eq(0), # disable DMA - videoctrl_stopping.eq(1), + If(~videoctrl & videoctrl_upd & ~videoctrl_starting & ~videoctrl_stopping, # neg edge + If(old_videoctrl, + self.video_framebuffer.fb_dma.enable.eq(0), # disable DMA + videoctrl_stopping.eq(1), + videoctrl_upd.eq(0), + ).Else( # already off, ignore? + videoctrl_upd.eq(0), + ) ), - If(~videoctrl & (self.video_framebuffer.fb_dma.rsv_level == 0) & (self.video_framebuffer.underflow), + If(videoctrl_stopping & (self.video_framebuffer.fb_dma.rsv_level == 0) & (self.video_framebuffer.underflow), vtg_enable.eq(0), # the DMA FIFO is purged, stop vtg + old_videoctrl.eq(0), videoctrl_stopping.eq(0), ), - ] - + ] + + self.comb += [ videoctrl_busy.eq(videoctrl_starting | videoctrl_stopping | in_reset | (post_reset_ctr != 0) | videoctrl_upd) ] + # VBL logic self.sync += [ If(self.video_framebuffer.vblping == 1,