updated ROM for automated makefile paramters, settable pixel clock

This commit is contained in:
Romain Dolbeau 2024-02-17 08:36:41 +01:00
parent e9af161c35
commit 76527c00d9
13 changed files with 756 additions and 137 deletions

4
.gitignore vendored
View File

@ -3,4 +3,6 @@ blit_goblin
blit_goblin.o
blit_goblin.s
blit_goblin_nubus.raw
blit_goblin_sbus.raw
blit_goblin_sbus.raw
**~
DeclROM/vid_decl_rom.elf

View File

@ -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

View File

@ -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")));

View File

@ -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;

View File

@ -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;
}

View File

@ -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); */

View File

@ -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;
}

View File

@ -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
};

17
DeclROM/gen_liteeth_param.sh Executable file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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,