From f867f02c83b4e63122d9595e0852a05e35745b4d Mon Sep 17 00:00:00 2001 From: Romain Dolbeau Date: Fri, 22 Apr 2022 22:00:25 +0100 Subject: [PATCH] add 16-bits/thousands of colors --- README.md | 2 +- nubus-to-ztex-gateware/DeclROM/DepVideo.inc | 2 + .../DeclROM/NuBusFPGADrvr.h | 1 + .../DeclROM/NuBusFPGADrvr_Ctrl.c | 19 ++++- .../DeclROM/NuBusFPGADrvr_Status.c | 15 +++- nubus-to-ztex-gateware/DeclROM/vid_decl_rom.s | 25 ++++++- nubus-to-ztex-gateware/goblin_fb.py | 72 +++++++++++++------ nubus-to-ztex-gateware/nubus_to_fpga_soc.py | 12 ++-- 8 files changed, 114 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 9540115..019d4ae 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,4 @@ The goal of this repository is to be able to interface a modern (2021 era) [FPGA ## Current status -First prototype is working, implements a basic single-resolution, 8-bits only framebuffer over HDMI or VGA. +First prototype is working in a Quadra 650. It implements a basic single-resolution, depth-switchable (1/2/4/8/16/32 bits) unaccellerated framebuffer over HDMI. The framebuffer can be used as secondary/primary/only framebuffer in the machine running OS8.1. QEmu tests indicate this should work with 7.1 & 7.5/7.6 as well. diff --git a/nubus-to-ztex-gateware/DeclROM/DepVideo.inc b/nubus-to-ztex-gateware/DeclROM/DepVideo.inc index 3f484bf..ff95236 100644 --- a/nubus-to-ztex-gateware/DeclROM/DepVideo.inc +++ b/nubus-to-ztex-gateware/DeclROM/DepVideo.inc @@ -9,6 +9,7 @@ Pages8s = 1 Pages4s = 1 Pages2s = 1 Pages1s = 1 +Pages15s = 1 Pages24s = 1 defmBounds_Ls = 0 @@ -23,6 +24,7 @@ RB8s = HRES RB4s = HRES/2 RB2s = HRES/4 RB1s = HRES/8 +RB15s = HRES*2 RB24s = HRES*4 DrHwNuBusFPGA = 0xBEEF /* placeholder */ diff --git a/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr.h b/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr.h index adbf97e..33b73f2 100644 --- a/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr.h +++ b/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr.h @@ -42,6 +42,7 @@ #define GOBOFB_MODE_4BIT 0x2 #define GOBOFB_MODE_8BIT 0x3 #define GOBOFB_MODE_24BIT 0x10 +#define GOBOFB_MODE_15BIT 0x11 struct MyGammaTbl { short gVersion; /*gamma version number*/ diff --git a/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr_Ctrl.c b/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr_Ctrl.c index 34ce2c2..b587186 100644 --- a/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr_Ctrl.c +++ b/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr_Ctrl.c @@ -164,6 +164,12 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) write_reg(dce, GOBOFB_MODE, GOBOFB_MODE_24BIT); SwapMMUMode ( &busMode ); break; + case sixthVidMode: + dStore->curMode = sixthVidMode; + SwapMMUMode ( &busMode ); + write_reg(dce, GOBOFB_MODE, GOBOFB_MODE_15BIT); + SwapMMUMode ( &busMode ); + break; default: return paramErr; } @@ -274,7 +280,7 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) return paramErr; SwapMMUMode ( &busMode ); - if (dStore->curMode != kDepthMode5) { + if ((dStore->curMode != kDepthMode5) && (dStore->curMode != kDepthMode6)) { /* grey the screen */ a32_l0 = a32; a32_l1 = a32 + wb; @@ -346,6 +352,8 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) break; case fifthVidMode: break; + case sixthVidMode: + break; default: return paramErr; } @@ -384,6 +392,11 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) write_reg(dce, GOBOFB_MODE, GOBOFB_MODE_24BIT); SwapMMUMode ( &busMode ); break; + case kDepthMode6: + SwapMMUMode ( &busMode ); + write_reg(dce, GOBOFB_MODE, GOBOFB_MODE_15BIT); + SwapMMUMode ( &busMode ); + break; default: return paramErr; } @@ -408,6 +421,8 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) break; case fifthVidMode: break; + case sixthVidMode: + break; default: return paramErr; } @@ -422,6 +437,8 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) break; case fifthVidMode: break; + case sixthVidMode: + break; default: return paramErr; } diff --git a/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr_Status.c b/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr_Status.c index bee43d2..75a7b20 100644 --- a/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr_Status.c +++ b/nubus-to-ztex-gateware/DeclROM/NuBusFPGADrvr_Status.c @@ -81,6 +81,8 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) break; case fifthVidMode: break; + case sixthVidMode: + break; default: return paramErr; } @@ -206,11 +208,11 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) break; case kDisplayModeIDFindFirstResolution: vdres->csDisplayModeID = firstVidMode; - vdres->csMaxDepthMode = kDepthMode5; + vdres->csMaxDepthMode = kDepthMode6; break; case kDisplayModeIDCurrent: vdres->csDisplayModeID = firstVidMode; - vdres->csMaxDepthMode = kDepthMode5; + vdres->csMaxDepthMode = kDepthMode6; break; default: return paramErr; @@ -230,7 +232,8 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) (vdparam->csDepthMode != kDepthMode2) && (vdparam->csDepthMode != kDepthMode3) && (vdparam->csDepthMode != kDepthMode4) && - (vdparam->csDepthMode != kDepthMode5)) + (vdparam->csDepthMode != kDepthMode5) && + (vdparam->csDepthMode != kDepthMode6)) return paramErr; VPBlock* vpblock = vdparam->csVPBlockPtr; /* basically the same as the EBVParms ? */ @@ -276,6 +279,12 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce) vpblock->vpPixelSize = 32; vpblock->vpCmpCount = 3; vpblock->vpCmpSize = 8; + } else if (vdparam->csDepthMode == kDepthMode6) { + vpblock->vpRowBytes = HRES*2; + vdparam->csDeviceType = directType; + vpblock->vpPixelSize = 16; + vpblock->vpCmpCount = 3; + vpblock->vpCmpSize = 5; } vpblock->vpPlaneBytes = 0; ret = noErr; diff --git a/nubus-to-ztex-gateware/DeclROM/vid_decl_rom.s b/nubus-to-ztex-gateware/DeclROM/vid_decl_rom.s index 756cf1e..0d740fb 100644 --- a/nubus-to-ztex-gateware/DeclROM/vid_decl_rom.s +++ b/nubus-to-ztex-gateware/DeclROM/vid_decl_rom.s @@ -99,7 +99,8 @@ _sRsrc_VidHiRes: OSLstEntry secondVidMode,_HiRes4Modes /* offset to 4 Bit Mode parms */ OSLstEntry thirdVidMode,_HiRes2Modes /* offset to 2 Bit Mode parms */ OSLstEntry fourthVidMode,_HiRes1Modes /* offset to 1 Bit Mode parms */ - OSLstEntry fifthVidMode,_HiRes24Modes /* offset to 24 Bit Mode parms */ + OSLstEntry fifthVidMode,_HiRes24Modes /* offset to 24/32 Bit Mode parms */ + OSLstEntry sixthVidMode,_HiRes15Modes /* offset to 1516 Bit Mode parms */ .long EndOfList /* end of list */ ALIGN 2 @@ -271,4 +272,26 @@ _HRV24Parms: .word 8 /* bmCmpSize */ .long defmPlaneBytes /* bmPlaneBytes */ _EndHRV24Parms: + ALIGN 2 +_HiRes15Modes: + OSLstEntry mVidParams,_HRV15Parms /* offset to vid parameters */ + DatLstEntry mPageCnt,Pages15s /* number of video pages */ + DatLstEntry mDevType,directType /* device type */ + .long EndOfList /* end of list */ +_HRV15Parms: + .long _EndHRV15Parms-_HRV15Parms /* physical block size */ + .long defmBaseOffset /* QuickDraw base offset ; vpBaseOffset */ + .word RB15s /* physRowBytes ; vpRowBytes */ + .word defmBounds_Ts,defmBounds_Ls,defmBounds_Bs,defmBounds_Rs /* vpBounds */ + .word defVersion /* bmVersion ; vpVersion */ + .word 0 /* packType not used ; vpPackType */ + .long 0 /* packSize not used ; vpPackSize */ + .long defmHRes /* bmHRes */ + .long defmVRes /* bmVRes */ + .word ChunkyDirect /* bmPixelType */ + .word 16 /* bmPixelSize */ + .word 3 /* bmCmpCount */ + .word 5 /* bmCmpSize */ + .long defmPlaneBytes /* bmPlaneBytes */ +_EndHRV15Parms: diff --git a/nubus-to-ztex-gateware/goblin_fb.py b/nubus-to-ztex-gateware/goblin_fb.py index b321de2..87f7da1 100644 --- a/nubus-to-ztex-gateware/goblin_fb.py +++ b/nubus-to-ztex-gateware/goblin_fb.py @@ -158,23 +158,45 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR): # we have 5 possible conversion and mux/connect the appropriate one if (truecolor): self.submodules.conv32 = ClockDomainsRenamer({"sys": clock_domain})(stream.Converter(dram_port.data_width, 32)) - handle_truecolor_sink = [ self.cdc.source.connect(self.conv32.sink) ] - handle_truecolor_source = [ source_buf_valid.eq(self.conv32.source.valid), - self.conv32.source.connect(source, keep={"ready"}), ] + self.submodules.conv16 = ClockDomainsRenamer({"sys": clock_domain})(stream.Converter(dram_port.data_width, 16)) + + handle_truecolor_sink = [ Case(self.indexed_mode, { + 0x0: [ self.cdc.source.connect(self.conv32.sink) ], + 0x1: [ self.cdc.source.connect(self.conv16.sink) ], + })] + + handle_truecolor_source = [ Case(self.indexed_mode, { + 0x0: [ source_buf_valid.eq(self.conv32.source.valid), self.conv32.source.connect(source, keep={"ready"}), ], + 0x1: [ source_buf_valid.eq(self.conv16.source.valid), self.conv16.source.connect(source, keep={"ready"}), ], + })] + if (endian == "big"): # this starts to _really_ mean "i'm in the SBusFPGA"... - handle_truecolor_databuf = [ data_buf_direct[0].eq(self.conv32.source.data[24:32]), - data_buf_direct[1].eq(self.conv32.source.data[16:24]), - data_buf_direct[2].eq(self.conv32.source.data[8:16]), ] + handle_truecolor_databuf = [ Case(self.indexed_mode, { + 0x0: [ data_buf_direct[0].eq(self.conv32.source.data[24:32]), + data_buf_direct[1].eq(self.conv32.source.data[16:24]), + data_buf_direct[2].eq(self.conv32.source.data[8:16]), ], + 0x1: [ data_buf_direct[0].eq(Cat(Signal(3, reset = 0), self.conv16.source.data[11:16])), # fixme: 16-bits in X11 ??? + data_buf_direct[1].eq(Cat(Signal(3, reset = 0), self.conv16.source.data[6:11])), + data_buf_direct[2].eq(Cat(Signal(3, reset = 0), self.conv16.source.data[1:6])), ] + })] else: - handle_truecolor_databuf = [ data_buf_direct[2].eq(self.conv32.source.data[24:32]), - data_buf_direct[1].eq(self.conv32.source.data[16:24]), - data_buf_direct[0].eq(self.conv32.source.data[8:16]), ] + handle_truecolor_databuf =[ Case(self.indexed_mode, { + 0x0: [ data_buf_direct[2].eq(self.conv32.source.data[24:32]), + data_buf_direct[1].eq(self.conv32.source.data[16:24]), + data_buf_direct[0].eq(self.conv32.source.data[8:16]), ], + 0x1: [ data_buf_direct[0].eq(Cat(self.conv16.source.data[ 4: 7], self.conv16.source.data[ 2: 7])), # 16-bits in QD32 + data_buf_direct[1].eq(Cat(self.conv16.source.data[15:16], self.conv16.source.data[ 0: 2], # seems byte-swapped in 5551 BGRx + self.conv16.source.data[13:16], self.conv16.source.data[ 0: 2])), + data_buf_direct[2].eq(Cat(self.conv16.source.data[10:13], self.conv16.source.data[ 8:13])), ] + })] + handle_truecolor_databuf_b = [ data_buf_b_direct[0].eq(data_buf_direct[0]), data_buf_b_direct[1].eq(data_buf_direct[1]), data_buf_b_direct[2].eq(data_buf_direct[2]), ] handle_truecolor_final_source = [ source_out_r.eq(data_buf_b_direct[2]), source_out_g.eq(data_buf_b_direct[1]), source_out_b.eq(data_buf_b_direct[0]), ] + else: handle_truecolor_sink = [ ] handle_truecolor_source = [ ] @@ -356,7 +378,7 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR): self.comb += self.vblping.eq(self.vbl_ps.o) class goblin(Module, AutoCSR): - def __init__(self, soc=None, phy=None, timings=None, clock_domain="sys", irq_line=None, endian="big", truecolor=True): + def __init__(self, soc=None, phy=None, timings=None, clock_domain="sys", irq_line=None, endian="big", hwcursor=True, truecolor=True): # 2 bits for color (0/r, 1/g, 2/b), 8 for @ and 8 for value self.submodules.upd_cmap_fifo = upd_cmap_fifo = ClockDomainsRenamer({"read": clock_domain, "write": "sys"})(AsyncFIFOBuffered(width=layout_len(cmap_layout), depth=8)) @@ -372,7 +394,7 @@ class goblin(Module, AutoCSR): name = "video_framebuffer" # near duplicate of plaform.add_video_framebuffer # Video Timing Generator. - vtg = FBVideoTimingGenerator(default_video_timings=timings if isinstance(timings, str) else timings[1], hwcursor=True) + vtg = FBVideoTimingGenerator(default_video_timings=timings if isinstance(timings, str) else timings[1], hwcursor=hwcursor) vtg = ClockDomainsRenamer(clock_domain)(vtg) setattr(self.submodules, f"{name}_vtg", vtg) vtg_enable = Signal(reset = 0) @@ -417,12 +439,17 @@ class goblin(Module, AutoCSR): soc.add_constant("VIDEO_FRAMEBUFFER_VRES", vres) # HW Cursor - - hwcursor_x = Signal(12) - hwcursor_y = Signal(12) - self.comb += vtg.hwcursor_x.eq(hwcursor_x) - self.comb += vtg.hwcursor_y.eq(hwcursor_y) + if (hwcursor): + hwcursor_x = Signal(12) + hwcursor_y = Signal(12) + self.comb += vtg.hwcursor_x.eq(hwcursor_x) + self.comb += vtg.hwcursor_y.eq(hwcursor_y) + handle_hwcursor = [ NextValue(hwcursor_x, bus.dat_w[16:28]), # FIXME: endianess + NextValue(hwcursor_y, bus.dat_w[ 0:12]), # FIXME: endianess + ] + else: + handle_hwcursor = [ ] self.bus = bus = wishbone.Interface() @@ -493,12 +520,10 @@ class goblin(Module, AutoCSR): }), ], # hw cursor x/y - 0x9: [ NextValue(hwcursor_x, bus.dat_w[16:28]), # FIXME: endianess - NextValue(hwcursor_y, bus.dat_w[ 0:12]), # FIXME: endianess - ], + 0x9: [ *handle_hwcursor ], }), Case(bus.adr[5:18], { - "default": [], + "default": [], # fixme: hwcursor for 0x1/0x2 0x1 : [ upd_overlay_fifo.we.eq(1), # 1*32 = 32..63 / 0x20..0x3F upd_overlay_fifo.din.eq(Cat(Signal(1, reset = 0), 31-bus.adr[0:5], bus.dat_w)) # FIXME: endianess ], @@ -540,7 +565,7 @@ class goblin(Module, AutoCSR): ), If(in_reset & ~vtg_enable, # we asked for a reset and by now, the VTG has been turned off (or was off) so we reset the DMA and change the parameters ##dma_reset.eq(1), # hpefully this will clear the FIFO as well - self.video_framebuffer.indexed_mode.eq(bt_mode[0:2] & ~(Replicate(bt_mode[4:5], 2))), + self.video_framebuffer.indexed_mode.eq(bt_mode[0:2]), # & ~(Replicate(bt_mode[4:5], 2)) *handle_truecolor_bit, in_reset.eq(0), post_reset_ctr.eq(7), @@ -550,7 +575,10 @@ class goblin(Module, AutoCSR): ##), If(post_reset_ctr == 4, # now reconfigure the DMA If(bt_mode[4:5], - self.video_framebuffer.fb_dma.length.eq(npixels * 4), + Case(bt_mode[0:2], { # fixme: truecolor + 0x0: self.video_framebuffer.fb_dma.length.eq(npixels * 4), + 0x1: self.video_framebuffer.fb_dma.length.eq(npixels * 2), + }), ).Else( Case(bt_mode[0:2], { 3: self.video_framebuffer.fb_dma.length.eq(npixels ), diff --git a/nubus-to-ztex-gateware/nubus_to_fpga_soc.py b/nubus-to-ztex-gateware/nubus_to_fpga_soc.py index 4123be0..bdef086 100644 --- a/nubus-to-ztex-gateware/nubus_to_fpga_soc.py +++ b/nubus-to-ztex-gateware/nubus_to_fpga_soc.py @@ -355,15 +355,15 @@ class NuBusFPGA(SoCCore): if (goblin): if (not hdmi): self.submodules.videophy = VideoVGAPHY(platform.request("vga"), clock_domain="vga") - self.submodules.goblin = goblin_fb.goblin(soc=self, phy=self.videophy, timings=goblin_res, clock_domain="vga", irq_line=self.platform.request("nmrq_3v3_n"), endian="little", truecolor=True) # clock_domain for the VGA side, goblin is running in cd_sys + self.submodules.goblin = goblin_fb.goblin(soc=self, phy=self.videophy, timings=goblin_res, clock_domain="vga", irq_line=self.platform.request("nmrq_3v3_n"), endian="little", hwcursor=False, truecolor=True) # clock_domain for the VGA side, goblin is running in cd_sys else: self.submodules.videophy = VideoS7HDMIPHY(platform.request("hdmi"), clock_domain="hdmi") - self.submodules.goblin = goblin_fb.goblin(soc=self, phy=self.videophy, timings=goblin_res, clock_domain="hdmi", irq_line=self.platform.request("nmrq_3v3_n"), endian="little", truecolor=True) # clock_domain for the HDMI side, goblin is running in cd_sys + self.submodules.goblin = goblin_fb.goblin(soc=self, phy=self.videophy, timings=goblin_res, clock_domain="hdmi", irq_line=self.platform.request("nmrq_3v3_n"), endian="little", hwcursor=False, truecolor=True) # clock_domain for the HDMI side, goblin is running in cd_sys self.bus.add_slave("goblin_bt", self.goblin.bus, SoCRegion(origin=self.mem_map.get("goblin_bt", None), size=0x1000, cached=False)) - pad_user_led_0 = platform.request("user_led", 0) - pad_user_led_1 = platform.request("user_led", 1) - self.comb += pad_user_led_0.eq(self.goblin.video_framebuffer.underflow) - self.comb += pad_user_led_1.eq(self.goblin.video_framebuffer.fb_dma.enable) + #pad_user_led_0 = platform.request("user_led", 0) + #pad_user_led_1 = platform.request("user_led", 1) + #self.comb += pad_user_led_0.eq(self.goblin.video_framebuffer.underflow) + #self.comb += pad_user_led_1.eq(self.goblin.video_framebuffer.fb_dma.enable) # for testing #from nubus_master_tst import PingMaster