diff --git a/docs/15khz-rgb-csync.md b/docs/15khz-rgb-csync.md new file mode 100644 index 000000000..714628522 --- /dev/null +++ b/docs/15khz-rgb-csync.md @@ -0,0 +1,98 @@ +# 15kHz RGB CSYNC Video Output + +## Overview + +The MEGA65 supports 15kHz RGB video output with composite sync (CSYNC) on the +VGA connector. This mode is designed for connecting to classic CRT monitors +like the Commodore 1084S, 1084S-D2, or any monitor that accepts 15kHz RGB with +composite sync input (commonly via SCART). + +In this mode: +- Horizontal sync rate: ~15.6kHz (PAL) or ~15.7kHz (NTSC) +- CSYNC is output on both VGA pin 13 and pin 14 (active low) +- RGB video uses the same pins as standard VGA + +The HDMI output continues to operate at 31kHz regardless of this setting. + +## Enabling 15kHz Mode + +15kHz mode is automatically detected via the VGA DDC pins (pins 12 and 15). +The MEGA65 drives pin 12 (SDA) LOW and senses pin 15 (SCL) with an internal pull-up. +Build a 15kHz cable with a **470Ω resistor** between VGA pins 12 and 15. +(The board has a strong 5V pull-up on pin 15, so a low value resistor is required.) + +| Cable Type | VGA Pin 15 State | Video Mode | +|----------------------------------|------------------|--------------------| +| Standard VGA cable | HIGH (pull-up) | Standard 31kHz VGA | +| 15kHz cable with resistor 12-15 | LOW (via resistor)| 15kHz RGB CSYNC | + +This detection happens at the hardware level, so 15kHz mode is active from +the very first frame - including the core selection menu and all boot screens. +Works on all MEGA65 board revisions (R3, R4, R5, R6). + +## Compatible Monitors + +This mode should work with monitors that accept 15kHz RGB with composite sync: +- Commodore 1084S, 1084S-D2 +- Sony PVM series (via RGB input) +- Any TV/monitor with SCART RGB input +- Arcade monitors (via appropriate adapter) + +At time of writing, this has been tested with a 1084S-D2 using an appropriate +cable. + +## VGA to DB-9 RGB Cable Wiring + +To connect the MEGA65 VGA output to a Commodore monitor with a DB-9 analog RGB +input (like the 1084S-D2), you need a custom cable with the following wiring: + +``` + VGA (DB-15 Male) DB-9 RGB (Male) + ================ =============== + + Pin 1 (Red) <----------------- Pin 3 (Red) + Pin 2 (Green) <----------------- Pin 4 (Green) + Pin 3 (Blue) <----------------- Pin 5 (Blue) + Pin 13 (HSYNC) <----------------- Pin 7 (Composite Sync) + + Pin 5 (GND) <----------------- Pin 1 (Ground) + Pin 6 (GND) <----------------- Pin 2 (Ground) + Pin 10 (GND) <----+ + +------------ Ground wire + + 15kHz Auto-Detection: + Pin 12 (SDA) <---[470Ω]---- Pin 15 (SCL) + + Not connected: + - DB-9 Pin 6 (Intensity) - not used + - DB-9 Pins 8, 9 - not used + + Note: CSYNC is available on both VGA pin 13 (HSYNC) and pin 14 (VSYNC) + for flexibility with different adapters. +``` + +The 470Ω resistor between VGA pins 12 and 15 enables automatic 15kHz mode detection. +Pin 12 is driven LOW by the MEGA65, and pin 15 has an external 5V pull-up on the board. +When the resistor bridges them, pin 15 is pulled below the logic threshold, enabling 15kHz mode. +Without this resistor, the MEGA65 outputs standard 31kHz VGA. + +## Interlace Support for V400 Modes + +When 15kHz mode is enabled and the VIC-IV is in V400 mode (e.g., 80x50 text mode), +interlace is automatically enabled. This ensures all 400 vertical lines are +displayed by alternating between odd and even fields on successive frames. + +Without interlace, V400 modes would only show every other scanline, resulting in +half the vertical resolution being lost. + +The interlace auto-enable only affects the 15kHz VGA output - HDMI output +continues to use progressive scan regardless of this setting. + +## Notes + +- The 15kHz mode uses the same PAL/NTSC timing as the internal video system, + so PAL regions get 50Hz and NTSC regions get 60Hz output. +- If your monitor loses sync briefly when switching between screens, this is + normal as the video mode stabilizes. +- Interlace mode can also be manually controlled via VIC-IV register `$D031` + bit 0 if needed for other purposes. diff --git a/src/vhdl/machine.vhdl b/src/vhdl/machine.vhdl index f3d169e9a..b462cc8e8 100644 --- a/src/vhdl/machine.vhdl +++ b/src/vhdl/machine.vhdl @@ -151,6 +151,14 @@ entity machine is luma : out unsigned(7 downto 0); chroma : out unsigned(7 downto 0); composite : out unsigned(7 downto 0); + + ---------------------------------------------------------------------- + -- 15kHz RGB with Composite Sync for retro CRT monitors + ---------------------------------------------------------------------- + rgb15khz_red : out unsigned(7 downto 0) := (others => '0'); + rgb15khz_green : out unsigned(7 downto 0) := (others => '0'); + rgb15khz_blue : out unsigned(7 downto 0) := (others => '0'); + rgb15khz_csync : out std_logic := '1'; -- Active low composite sync ---------------------------------------------------------------------- -- VGA output @@ -161,6 +169,9 @@ entity machine is lcd_vsync : out std_logic := '0'; pal50_select_out : out std_logic := '0'; vga_blank : out std_logic := '0'; + + -- 15kHz RGB CSYNC mode selection (active high enables 15kHz mode) + vga_15khz_csync_mode : in std_logic := '0'; vgared : out UNSIGNED (7 downto 0) := x"00"; vgagreen : out UNSIGNED (7 downto 0) := x"00"; @@ -417,6 +428,10 @@ entity machine is dipsw : in std_logic_vector(4 downto 0) := (others => '0'); sw : in std_logic_vector(15 downto 0); btn : in std_logic_vector(4 downto 0); + + -- DIP switch 3 directly exposed for 15kHz RGB CSYNC mode control + -- (active high = enable 15kHz mode) + dipsw3_out : out std_logic := '0'; UART_TXD : out std_logic := '1'; RsRx : in std_logic; @@ -837,6 +852,7 @@ architecture Behavioral of machine is signal interlace_mode : std_logic; signal mono_mode : std_logic; + signal v400_mode : std_logic; begin @@ -881,6 +897,9 @@ begin -- LED indication for when eth remote control is enabled -- (requires DIPSW 2 and MEGA+SHIFT+POUND) eth_load_enabled <= eth_load_enable and dipsw_int(1); + + -- Expose DIP switch 3 for 15kHz RGB CSYNC mode control + dipsw3_out <= dipsw_int(3); -- Latch reset from monitor interface to avoid tripping on glitches -- But requiring to be low so long causes monitor induced reset to be ignored. @@ -1295,6 +1314,8 @@ begin interlace_mode => interlace_mode, mono_mode => mono_mode, + v400_mode => v400_mode, + vga_15khz_csync_mode => vga_15khz_csync_mode, -- Framing information for VIC-IV x_zero => external_frame_x_zero, @@ -1324,6 +1345,12 @@ begin luma => luma, chroma => chroma, composite => composite, + + -- 15kHz RGB with composite sync + rgb15khz_red => rgb15khz_red, + rgb15khz_green => rgb15khz_green, + rgb15khz_blue => rgb15khz_blue, + rgb15khz_csync => rgb15khz_csync, -- And the variations on those signals for the LCD display lcd_hsync => lcd_hsync, @@ -1354,6 +1381,7 @@ begin interlace_mode => interlace_mode, mono_mode => mono_mode, + v400_mode => v400_mode, hypervisor_mode => cpu_hypervisor_mode, diff --git a/src/vhdl/mega65r3.vhdl b/src/vhdl/mega65r3.vhdl index e53d6725b..57366490c 100644 --- a/src/vhdl/mega65r3.vhdl +++ b/src/vhdl/mega65r3.vhdl @@ -142,6 +142,11 @@ entity container is vgared : out UNSIGNED (7 downto 0); vgagreen : out UNSIGNED (7 downto 0); vgablue : out UNSIGNED (7 downto 0); + -- VGA DDC pins for 15kHz mode detection + -- VGA DDC pins for 15kHz mode detection + -- 470Ω resistor between pins 12-15 enables 15kHz mode + vga_sda : out std_logic; + vga_scl : in std_logic; TMDS_data_p : out STD_LOGIC_VECTOR(2 downto 0); TMDS_data_n : out STD_LOGIC_VECTOR(2 downto 0); @@ -473,11 +478,25 @@ architecture Behavioral of container is signal luma : unsigned(7 downto 0); signal chroma : unsigned(7 downto 0); signal composite : unsigned(7 downto 0); + + -- 15kHz RGB with composite sync signals + signal rgb15khz_red : unsigned(7 downto 0); + signal rgb15khz_green : unsigned(7 downto 0); + signal rgb15khz_blue : unsigned(7 downto 0); + signal rgb15khz_csync : std_logic; + signal vga_15khz_mode : std_logic := '0'; + + -- Hardware detection for 15kHz mode via VGA DDC pins + -- SDA driven LOW, SCL sensed - 470Ω resistor between pins 12-15 pulls SCL low + signal vga_15khz_detect : std_logic := '0'; signal eth_load_enable : std_logic; begin + -- Drive VGA SDA low for 15kHz detection (concurrent assignment) + vga_sda <= '0'; + --STARTUPE2:STARTUPBlock--7Series --XilinxHDLLibrariesGuide,version2012.4 @@ -896,6 +915,15 @@ begin chroma => chroma, composite => composite, + -- 15kHz RGB with composite sync + rgb15khz_red => rgb15khz_red, + rgb15khz_green => rgb15khz_green, + rgb15khz_blue => rgb15khz_blue, + rgb15khz_csync => rgb15khz_csync, + + -- 15kHz RGB CSYNC mode control (active high = 15kHz mode) + vga_15khz_csync_mode => vga_15khz_mode, + vsync => v_vsync, vga_hsync => v_vga_hsync, hdmi_hsync => v_hdmi_hsync, @@ -1078,6 +1106,7 @@ begin sw => sw, dipsw => dipsw, + dipsw3_out => open, -- uart_rx => '1', btn => (others => '1') @@ -1105,7 +1134,12 @@ begin vdac_blank_n <= '1'; -- was: not (v_hsync or v_vsync); -- VGA output at full pixel clock - vdac_clk <= pixelclock; + -- In 15kHz mode, use clock27 to match the 15kHz signal source + if vga_15khz_mode = '1' then + vdac_clk <= clock27; + else + vdac_clk <= pixelclock; + end if; -- Use both real and cartridge IRQ and NMI signals irq_combined <= irq and irq_out; @@ -1129,6 +1163,12 @@ begin dvi_select <= portp_drive(1); + -- 15kHz RGB CSYNC mode detection via VGA DDC pins + -- SDA (pin 12) driven LOW, SCL (pin 15) sensed + -- 470Ω resistor between pins 12-15 pulls SCL below threshold = 15kHz mode + vga_15khz_detect <= not vga_scl; + vga_15khz_mode <= vga_15khz_detect; + reset_high <= not btncpureset; btncpureset <= max10_reset_out; @@ -1230,11 +1270,29 @@ begin led <= portp_drive(4); if rising_edge(pixelclock) then - hsync <= v_vga_hsync; - vsync <= v_vsync; - vgared <= v_red; - vgagreen <= v_green; - vgablue <= v_blue; + -- VGA output selection: 31kHz VGA or 15kHz RGB CSYNC + -- When 15kHz mode is enabled (vga_15khz_mode='1'): + -- - hsync outputs CSYNC (active low composite sync) + -- - vsync is held HIGH (not connected/used in 15kHz mode) + -- - RGB comes from 15kHz raster buffer + -- HDMI output always uses 31kHz signals regardless of VGA mode + if vga_15khz_mode = '1' then + -- 15kHz RGB CSYNC mode for retro CRT monitors + hsync <= rgb15khz_csync; -- CSYNC on VGA pin 13 (active low) + vsync <= rgb15khz_csync; -- CSYNC also on VGA pin 14 for flexibility + vgared <= rgb15khz_red; + vgagreen <= rgb15khz_green; + vgablue <= rgb15khz_blue; + else + -- Standard 31kHz VGA mode + hsync <= v_vga_hsync; + vsync <= v_vsync; + vgared <= v_red; + vgagreen <= v_green; + vgablue <= v_blue; + end if; + + -- HDMI output always uses 31kHz (unaffected by VGA mode selection) hdmired <= v_red; hdmigreen <= v_green; hdmiblue <= v_blue; diff --git a/src/vhdl/mega65r3.xdc b/src/vhdl/mega65r3.xdc index 38486cf64..3e314b29c 100644 --- a/src/vhdl/mega65r3.xdc +++ b/src/vhdl/mega65r3.xdc @@ -228,6 +228,12 @@ set_property -dict {PACKAGE_PIN F15 IOSTANDARD LVCMOS33} [get_ports fb_fire] ##VGA Connector +# VGA I2C bus - used for 15kHz mode detection +# SDA (pin 12) driven LOW, SCL (pin 15) sensed with pull-up +# If resistor bridges pins 12-15, SCL reads LOW = 15kHz mode +set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports vga_sda] +set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33 PULLUP TRUE} [get_ports vga_scl] + # XXX - Is this needed? set_property -dict {PACKAGE_PIN AA9 IOSTANDARD LVCMOS33} [get_ports vdac_clk] set_property -dict {PACKAGE_PIN V10 IOSTANDARD LVCMOS33} [get_ports vdac_sync_n] diff --git a/src/vhdl/mega65r4.vhdl b/src/vhdl/mega65r4.vhdl index 101266f8a..90d797b9b 100644 --- a/src/vhdl/mega65r4.vhdl +++ b/src/vhdl/mega65r4.vhdl @@ -167,6 +167,11 @@ entity container is vgared : out UNSIGNED (7 downto 0); vgagreen : out UNSIGNED (7 downto 0); vgablue : out UNSIGNED (7 downto 0); + -- VGA DDC pins for 15kHz mode detection + -- VGA DDC pins for 15kHz mode detection + -- 470Ω resistor between pins 12-15 enables 15kHz mode + vga_sda : out std_logic; + vga_scl : in std_logic; TMDS_data_p : out STD_LOGIC_VECTOR(2 downto 0); TMDS_data_n : out STD_LOGIC_VECTOR(2 downto 0); @@ -531,6 +536,17 @@ architecture Behavioral of container is signal luma : unsigned(7 downto 0); signal chroma : unsigned(7 downto 0); signal composite : unsigned(7 downto 0); + + -- 15kHz RGB with composite sync signals + signal rgb15khz_red : unsigned(7 downto 0); + signal rgb15khz_green : unsigned(7 downto 0); + signal rgb15khz_blue : unsigned(7 downto 0); + signal rgb15khz_csync : std_logic; + signal vga_15khz_mode : std_logic := '0'; + + -- Hardware detection for 15kHz mode via VGA DDC pins + -- SDA driven LOW, SCL sensed - 470Ω resistor between pins 12-15 pulls SCL low + signal vga_15khz_detect : std_logic := '0'; signal eth_load_enable : std_logic; @@ -542,6 +558,9 @@ architecture Behavioral of container is begin + -- Drive VGA SDA low for 15kHz detection (concurrent assignment) + vga_sda <= '0'; + --STARTUPE2:STARTUPBlock--7Series --XilinxHDLLibrariesGuide,version2012.4 @@ -1024,6 +1043,9 @@ begin pal50_select_out => pal50, upscale_enable => upscale_enable, + + -- 15kHz RGB CSYNC mode control (active high = 15kHz mode) + vga_15khz_csync_mode => vga_15khz_mode, hyper_addr => hyper_addr, hyper_request_toggle => hyper_request_toggle, @@ -1067,6 +1089,12 @@ begin luma => luma, chroma => chroma, composite => composite, + + -- 15kHz RGB with composite sync + rgb15khz_red => rgb15khz_red, + rgb15khz_green => rgb15khz_green, + rgb15khz_blue => rgb15khz_blue, + rgb15khz_csync => rgb15khz_csync, vsync => v_vsync, vga_hsync => v_vga_hsync, @@ -1263,6 +1291,7 @@ begin sw => sw, dipsw(4) => '0', dipsw(3 downto 0) => dipsw, + dipsw3_out => open, -- uart_rx => '1', btn => (others => '1') @@ -1308,7 +1337,10 @@ begin end if; -- VGA output at full pixel clock - if upscale_enable = '0' then + -- In 15kHz mode, use clock27 to match the 15kHz signal source + if vga_15khz_mode = '1' then + vdac_clk_i <= clock27; + elsif upscale_enable = '0' then vdac_clk_i <= pixelclock; else vdac_clk_i <= clock74p22; @@ -1336,6 +1368,13 @@ begin portp_drive <= portp; dvi_select <= portp_drive(1); + + -- 15kHz RGB CSYNC mode detection via VGA DDC pins + -- 15kHz RGB CSYNC mode detection via VGA DDC pins + -- SDA (pin 12) driven LOW, SCL (pin 15) sensed + -- 470Ω resistor between pins 12-15 pulls SCL below threshold = 15kHz mode + vga_15khz_detect <= not vga_scl; + vga_15khz_mode <= vga_15khz_detect; -- btncpureset is active low -- reset_high is active high @@ -1490,11 +1529,29 @@ begin -- LED on main board led <= portp_drive(4); - hsync <= up_vga_hsync; - vsync <= up_vsync; - vgared <= up_red; - vgagreen <= up_green; - vgablue <= up_blue; + -- VGA output selection: 31kHz VGA or 15kHz RGB CSYNC + -- When 15kHz mode is enabled (vga_15khz_mode='1'): + -- - hsync outputs CSYNC (active low composite sync) + -- - vsync is held HIGH (not connected/used in 15kHz mode) + -- - RGB comes from 15kHz raster buffer + -- HDMI output always uses 31kHz signals regardless of VGA mode + if vga_15khz_mode = '1' then + -- 15kHz RGB CSYNC mode for retro CRT monitors + hsync <= rgb15khz_csync; -- CSYNC on VGA pin 13 (active low) + vsync <= rgb15khz_csync; -- CSYNC also on VGA pin 14 for flexibility + vgared <= rgb15khz_red; + vgagreen <= rgb15khz_green; + vgablue <= rgb15khz_blue; + else + -- Standard 31kHz VGA mode + hsync <= up_vga_hsync; + vsync <= up_vsync; + vgared <= up_red; + vgagreen <= up_green; + vgablue <= up_blue; + end if; + + -- HDMI output always uses 31kHz (unaffected by VGA mode selection) hdmired <= v_red; hdmigreen <= v_green; hdmiblue <= v_blue; diff --git a/src/vhdl/mega65r4.xdc b/src/vhdl/mega65r4.xdc index 7c8c50fe4..3af2769e8 100644 --- a/src/vhdl/mega65r4.xdc +++ b/src/vhdl/mega65r4.xdc @@ -178,9 +178,11 @@ set_property -dict {PACKAGE_PIN N19 IOSTANDARD LVCMOS33} [get_ports fb_fire_drai ##VGA Connector -# VGA I2C bus -# set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports vga_sda] -# set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33} [get_ports vga_scl] +# VGA I2C bus - used for 15kHz mode detection +# SDA (pin 12) driven LOW, SCL (pin 15) sensed with pull-up +# If resistor bridges pins 12-15, SCL reads LOW = 15kHz mode +set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports vga_sda] +set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33 PULLUP TRUE} [get_ports vga_scl] # XXX - Is this needed? set_property -dict {PACKAGE_PIN AA9 IOSTANDARD LVCMOS33} [get_ports vdac_clk] diff --git a/src/vhdl/mega65r5.vhdl b/src/vhdl/mega65r5.vhdl index 8253f843d..babf7aab0 100644 --- a/src/vhdl/mega65r5.vhdl +++ b/src/vhdl/mega65r5.vhdl @@ -552,6 +552,17 @@ architecture Behavioral of container is signal luma : unsigned(7 downto 0); signal chroma : unsigned(7 downto 0); signal composite : unsigned(7 downto 0); + + -- 15kHz RGB with composite sync signals + signal rgb15khz_red : unsigned(7 downto 0); + signal rgb15khz_green : unsigned(7 downto 0); + signal rgb15khz_blue : unsigned(7 downto 0); + signal rgb15khz_csync : std_logic; + signal vga_15khz_mode : std_logic := '0'; + + -- Hardware detection for 15kHz mode via VGA DDC pins + -- SDA driven LOW, SCL sensed - 470Ω resistor between pins 12-15 pulls SCL low + signal vga_15khz_detect : std_logic := '0'; signal eth_load_enable : std_logic; @@ -563,6 +574,9 @@ architecture Behavioral of container is begin + -- Drive VGA SDA low for 15kHz detection (concurrent assignment) + vga_sda <= '0'; + --STARTUPE2:STARTUPBlock--7Series --XilinxHDLLibrariesGuide,version2012.4 @@ -1057,6 +1071,9 @@ begin pal50_select_out => pal50, upscale_enable => upscale_enable, + + -- 15kHz RGB CSYNC mode control (active high = 15kHz mode) + vga_15khz_csync_mode => vga_15khz_mode, hyper_addr => hyper_addr, hyper_request_toggle => hyper_request_toggle, @@ -1100,6 +1117,12 @@ begin luma => luma, chroma => chroma, composite => composite, + + -- 15kHz RGB with composite sync + rgb15khz_red => rgb15khz_red, + rgb15khz_green => rgb15khz_green, + rgb15khz_blue => rgb15khz_blue, + rgb15khz_csync => rgb15khz_csync, vsync => v_vsync, vga_hsync => v_vga_hsync, @@ -1296,6 +1319,7 @@ begin sw => sw, dipsw(4 downto 0) => (others => '0'), + dipsw3_out => open, -- uart_rx => '1', btn => (others => '1') @@ -1342,7 +1366,10 @@ begin end if; -- VGA output at full pixel clock - if upscale_enable = '0' then + -- In 15kHz mode, use clock27 to match the 15kHz signal source + if vga_15khz_mode = '1' then + vdac_clk_i <= clock27; + elsif upscale_enable = '0' then vdac_clk_i <= pixelclock; else vdac_clk_i <= clock74p22; @@ -1388,6 +1415,13 @@ begin portp_drive <= portp; dvi_select <= portp_drive(1); + + -- 15kHz RGB CSYNC mode detection via VGA DDC pins + -- 15kHz RGB CSYNC mode detection via VGA DDC pins + -- SDA (pin 12) driven LOW, SCL (pin 15) sensed + -- 470Ω resistor between pins 12-15 pulls SCL below threshold = 15kHz mode + vga_15khz_detect <= not vga_scl; + vga_15khz_mode <= vga_15khz_detect; -- btncpureset is active low -- reset_high is active high @@ -1542,11 +1576,29 @@ begin -- LED on main board led <= portp_drive(4); - hsync <= up_vga_hsync; - vsync <= up_vsync; - vgared <= up_red; - vgagreen <= up_green; - vgablue <= up_blue; + -- VGA output selection: 31kHz VGA or 15kHz RGB CSYNC + -- When 15kHz mode is enabled (vga_15khz_mode='1'): + -- - hsync outputs CSYNC (active low composite sync) + -- - vsync is held HIGH (not connected/used in 15kHz mode) + -- - RGB comes from 15kHz raster buffer + -- HDMI output always uses 31kHz signals regardless of VGA mode + if vga_15khz_mode = '1' then + -- 15kHz RGB CSYNC mode for retro CRT monitors + hsync <= rgb15khz_csync; -- CSYNC on VGA pin 13 (active low) + vsync <= rgb15khz_csync; -- CSYNC also on VGA pin 14 for flexibility + vgared <= rgb15khz_red; + vgagreen <= rgb15khz_green; + vgablue <= rgb15khz_blue; + else + -- Standard 31kHz VGA mode + hsync <= up_vga_hsync; + vsync <= up_vsync; + vgared <= up_red; + vgagreen <= up_green; + vgablue <= up_blue; + end if; + + -- HDMI output always uses 31kHz (unaffected by VGA mode selection) hdmired <= v_red; hdmigreen <= v_green; hdmiblue <= v_blue; diff --git a/src/vhdl/mega65r5.xdc b/src/vhdl/mega65r5.xdc index f0d0def87..10dda5934 100644 --- a/src/vhdl/mega65r5.xdc +++ b/src/vhdl/mega65r5.xdc @@ -179,9 +179,11 @@ set_property -dict {PACKAGE_PIN N19 IOSTANDARD LVCMOS33} [get_ports fb_fire_drai ##VGA Connector -# VGA I2C bus -# set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports vga_sda] -# set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33} [get_ports vga_scl] +# VGA I2C bus - used for 15kHz mode detection +# SDA (pin 12) driven LOW, SCL (pin 15) sensed with pull-up +# If resistor bridges pins 12-15, SCL reads LOW = 15kHz mode +set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports vga_sda] +set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33 PULLUP TRUE} [get_ports vga_scl] # XXX - Is this needed? set_property -dict {PACKAGE_PIN AA9 IOSTANDARD LVCMOS33} [get_ports vdac_clk] diff --git a/src/vhdl/mega65r6.vhdl b/src/vhdl/mega65r6.vhdl index da10051d5..5d0ee2341 100644 --- a/src/vhdl/mega65r6.vhdl +++ b/src/vhdl/mega65r6.vhdl @@ -179,6 +179,10 @@ entity container is vgared : out UNSIGNED (7 downto 0); vgagreen : out UNSIGNED (7 downto 0); vgablue : out UNSIGNED (7 downto 0); + -- VGA DDC pins for 15kHz mode detection + -- 470Ω resistor between pins 12-15 enables 15kHz mode + vga_sda : out std_logic; + vga_scl : in std_logic; TMDS_data_p : out STD_LOGIC_VECTOR(2 downto 0); TMDS_data_n : out STD_LOGIC_VECTOR(2 downto 0); @@ -552,6 +556,17 @@ architecture Behavioral of container is signal luma : unsigned(7 downto 0); signal chroma : unsigned(7 downto 0); signal composite : unsigned(7 downto 0); + + -- 15kHz RGB with composite sync signals + signal rgb15khz_red : unsigned(7 downto 0); + signal rgb15khz_green : unsigned(7 downto 0); + signal rgb15khz_blue : unsigned(7 downto 0); + signal rgb15khz_csync : std_logic; + signal vga_15khz_mode : std_logic := '0'; + + -- Hardware detection for 15kHz mode via VGA DDC pins + -- SDA driven LOW, SCL sensed - 470Ω resistor between pins 12-15 pulls SCL low + signal vga_15khz_detect : std_logic := '0'; signal eth_load_enable : std_logic; @@ -563,6 +578,9 @@ architecture Behavioral of container is begin + -- Drive VGA SDA low for 15kHz detection + vga_sda <= '0'; + --STARTUPE2:STARTUPBlock--7Series --XilinxHDLLibrariesGuide,version2012.4 @@ -1051,6 +1069,9 @@ begin pal50_select_out => pal50, upscale_enable => upscale_enable, + + -- 15kHz RGB CSYNC mode control (active high = 15kHz mode) + vga_15khz_csync_mode => vga_15khz_mode, hyper_addr => hyper_addr, hyper_request_toggle => hyper_request_toggle, @@ -1094,6 +1115,12 @@ begin luma => luma, chroma => chroma, composite => composite, + + -- 15kHz RGB with composite sync + rgb15khz_red => rgb15khz_red, + rgb15khz_green => rgb15khz_green, + rgb15khz_blue => rgb15khz_blue, + rgb15khz_csync => rgb15khz_csync, vsync => v_vsync, vga_hsync => v_vga_hsync, @@ -1290,6 +1317,7 @@ begin sw => sw, dipsw(4 downto 0) => (others => '0'), + dipsw3_out => open, -- uart_rx => '1', btn => (others => '1') @@ -1336,7 +1364,10 @@ begin end if; -- VGA output at full pixel clock - if upscale_enable = '0' then + -- In 15kHz mode, use clock27 to match the 15kHz signal source + if vga_15khz_mode = '1' then + vdac_clk_i <= clock27; + elsif upscale_enable = '0' then vdac_clk_i <= pixelclock; else vdac_clk_i <= clock74p22; @@ -1382,6 +1413,12 @@ begin portp_drive <= portp; dvi_select <= portp_drive(1); + + -- 15kHz RGB CSYNC mode detection via VGA DDC pins + -- SDA (pin 12) driven LOW, SCL (pin 15) sensed + -- 470Ω resistor between pins 12-15 pulls SCL below threshold = 15kHz mode + vga_15khz_detect <= not vga_scl; + vga_15khz_mode <= vga_15khz_detect; -- btncpureset is active low -- reset_high is active high @@ -1536,11 +1573,29 @@ begin -- LED on main board led <= portp_drive(4); - hsync <= up_vga_hsync; - vsync <= up_vsync; - vgared <= up_red; - vgagreen <= up_green; - vgablue <= up_blue; + -- VGA output selection: 31kHz VGA or 15kHz RGB CSYNC + -- When 15kHz mode is enabled (vga_15khz_mode='1'): + -- - hsync outputs CSYNC (active low composite sync) + -- - vsync is held HIGH (not connected/used in 15kHz mode) + -- - RGB comes from 15kHz raster buffer + -- HDMI output always uses 31kHz signals regardless of VGA mode + if vga_15khz_mode = '1' then + -- 15kHz RGB CSYNC mode for retro CRT monitors + hsync <= rgb15khz_csync; -- CSYNC on VGA pin 13 (active low) + vsync <= rgb15khz_csync; -- CSYNC also on VGA pin 14 for flexibility + vgared <= rgb15khz_red; + vgagreen <= rgb15khz_green; + vgablue <= rgb15khz_blue; + else + -- Standard 31kHz VGA mode + hsync <= up_vga_hsync; + vsync <= up_vsync; + vgared <= up_red; + vgagreen <= up_green; + vgablue <= up_blue; + end if; + + -- HDMI output always uses 31kHz (unaffected by VGA mode selection) hdmired <= v_red; hdmigreen <= v_green; hdmiblue <= v_blue; diff --git a/src/vhdl/pixel_driver.vhdl b/src/vhdl/pixel_driver.vhdl index b0cd472bf..02098d757 100644 --- a/src/vhdl/pixel_driver.vhdl +++ b/src/vhdl/pixel_driver.vhdl @@ -57,6 +57,10 @@ entity pixel_driver is interlace_mode : in std_logic := '1'; mono_mode : in std_logic := '0'; + v400_mode : in std_logic := '0'; + + -- 15kHz RGB CSYNC mode for VGA output (active high enables 15kHz mode) + vga_15khz_csync_mode : in std_logic := '0'; -- ~1mhz clock for CPU and other parts, derived directly from the video clock phi_1mhz_ntsc_out : out std_logic; @@ -89,6 +93,12 @@ entity pixel_driver is chroma : out unsigned(7 downto 0) := (others => '0'); composite : out unsigned(7 downto 0) := (others => '0'); + -- 15kHz RGB output with composite sync for retro CRT monitors + rgb15khz_red : out unsigned(7 downto 0) := (others => '0'); + rgb15khz_green : out unsigned(7 downto 0) := (others => '0'); + rgb15khz_blue : out unsigned(7 downto 0) := (others => '0'); + rgb15khz_csync : out std_logic := '1'; -- Active low composite sync + -- Inform VIC-IV of new rasters and new frames -- Signals for VIC-IV etc to know what is happening hsync_uninverted : out std_logic := '0'; @@ -132,6 +142,10 @@ architecture greco_roman of pixel_driver is signal last_interlace : std_logic := '0'; signal interlace_mode_integer : integer range 0 to 1 := 0; + + -- Auto-enable interlace for 15kHz mode when V400 is active + -- This ensures 80x50 and other V400 modes display correctly on 15kHz CRT monitors + signal interlace_15khz : std_logic := '0'; -- Set to 1 to enable gamma pre-correction of video signal gamma_enabled : std_logic := '0'; @@ -311,6 +325,12 @@ architecture greco_roman of pixel_driver is signal cv_sync_hsrc : std_logic; signal cv_active_area : std_logic := '0'; + -- 15kHz RGB output signals (active low csync) + signal rgb15khz_red_int : unsigned(7 downto 0) := (others => '0'); + signal rgb15khz_green_int : unsigned(7 downto 0) := (others => '0'); + signal rgb15khz_blue_int : unsigned(7 downto 0) := (others => '0'); + signal rgb15khz_csync_int : std_logic := '1'; + signal x_zero_last : std_logic := '0'; signal y_zero_last : std_logic := '0'; signal x_zero_int : std_logic; @@ -925,6 +945,10 @@ begin interlace_mode_integer <= 0; end if; + -- Auto-enable interlace for 15kHz mode when V400 is active + -- This ensures 80x50 and other V400 modes display all scanlines on 15kHz CRT monitors + interlace_15khz <= interlace_mode or (vga_15khz_csync_mode and v400_mode); + if debug_forward /= last_debug_forward then last_debug_forward <= debug_forward; if mono_mode='0' then @@ -962,7 +986,7 @@ begin vblank_train_len_adjust <= 3; end if; else - if interlace_mode='1' then + if interlace_15khz='1' then if field_is_odd=0 then vblank_train_len_adjust <= 1; else @@ -982,10 +1006,11 @@ begin report "Start of frame detected. field_is_odd was " & integer'image(field_is_odd); -- Start of new frame -- toggle field_is_odd -- Interlace mode controls if we always show the same field or alternate + -- Use interlace_15khz to auto-enable interlace for 15kHz + V400 modes -- XXX mono_mode for debug currently selects which of those two fields -- will be shown in non-interlace mode if mono_mode='0' then - if field_is_odd = 1 and interlace_mode='1' then + if field_is_odd = 1 and interlace_15khz='1' then report "Setting field_is_odd to 0"; field_is_odd <= 0; else @@ -993,7 +1018,7 @@ begin field_is_odd <= 1; end if; else - if field_is_odd = 0 and interlace_mode='1' then + if field_is_odd = 0 and interlace_15khz='1' then report "Setting field_is_odd to 1"; field_is_odd <= 1; else @@ -1194,7 +1219,7 @@ begin if cv_vsync_row = 1 then -- Set PAL V invert based on which field we are in - if interlace_mode='0' then + if interlace_15khz='0' then -- For non-interlaced mode, all fields are the same -- Suppress colour burst on the first raster of the frame -- according to Demystifying Video 4th Ed Fig 8.18 @@ -1229,7 +1254,7 @@ begin end if; if pal50_select_internal='1' then -- PAL - if interlace_mode='0' then + if interlace_15khz='0' then cv_sync <= not pal_progressive_vblanks(cv_vsync_row)(vsync_xpos_max - vsync_xpos); else if field_is_odd=1 then @@ -1577,8 +1602,35 @@ begin time_since_last_pixel <= 0; end if; + -- ========================================================================= + -- 15kHz RGB Output Generation + -- Uses the same RGB data and composite sync as composite video output + -- cv_sync is already the proper composite sync with PAL/NTSC timing, + -- serration pulses, and equalization pulses + -- ========================================================================= + if vga_15khz_csync_mode = '1' then + -- Output 15kHz RGB with composite sync + rgb15khz_red_int <= cv_red; + rgb15khz_green_int <= cv_green; + rgb15khz_blue_int <= cv_blue; + -- cv_sync is active high during sync pulse, but VGA expects active low + rgb15khz_csync_int <= not cv_sync; + else + -- Default: output black with sync high (inactive) + rgb15khz_red_int <= (others => '0'); + rgb15khz_green_int <= (others => '0'); + rgb15khz_blue_int <= (others => '0'); + rgb15khz_csync_int <= '1'; + end if; + end if; end process; + + -- Drive the 15kHz RGB output ports + rgb15khz_red <= rgb15khz_red_int; + rgb15khz_green <= rgb15khz_green_int; + rgb15khz_blue <= rgb15khz_blue_int; + rgb15khz_csync <= rgb15khz_csync_int; end greco_roman; diff --git a/src/vhdl/viciv.vhdl b/src/vhdl/viciv.vhdl index cd013b0ce..be059fead 100644 --- a/src/vhdl/viciv.vhdl +++ b/src/vhdl/viciv.vhdl @@ -107,6 +107,7 @@ entity viciv is interlace_mode : out std_logic := '0'; mono_mode : out std_logic := '0'; + v400_mode : out std_logic := '0'; -- Used to tell the CPU when to steal cycles to simulate badlines badline_toggle : out std_logic := '0'; @@ -2104,6 +2105,7 @@ begin interlace_mode <= reg_interlace; mono_mode <= reg_mono; + v400_mode <= reg_v400; last_dd00_bits <= dd00_bits; if last_dd00_bits /= dd00_bits then diff --git a/vivado_wrapper b/vivado_wrapper index 202f42cdb..74915f24c 100755 --- a/vivado_wrapper +++ b/vivado_wrapper @@ -1,15 +1,52 @@ #!/bin/bash -# Check if VIVADODIR is empty or undefined and detect version instead +# Auto-detect Vivado installation +# Searches common locations and finds the latest version. +# If your installation ends up in another location, add it to +# the search_paths array. For some reason different versions +# install in different locations. + +find_vivado() { + local search_paths=( + "/opt/Xilinx/Vivado" + "/opt/Xilinx" + "/opt/Xilinx/2025.2" + "/opt/Xilinx/2025.2/Vivado" + ) + + # If VIVADODIR is already set and valid, use it + if [ -n "$VIVADODIR" ] && [ -f "$VIVADODIR/settings64.sh" ]; then + echo "$VIVADODIR" + return 0 + fi + + # Check each path for settings64.sh + for path in "${search_paths[@]}"; do + if [ -f "$path/settings64.sh" ]; then + echo "$path" + return 0 + fi + done + + return 1 +} + +VIVADODIR=$(find_vivado) + if [ -z "$VIVADODIR" ]; then - # workaround for wrongly chosen VIVADO install path - if [ -e /opt/Xilinx/Vivado/Vivado ]; then - VIVADODIR=`/bin/ls -1d /opt/Xilinx/Vivado/Vivado/* /opt/Xilinx/Vivado/* | sort | grep -v "Vivado/xic" | tail -1` - else - VIVADODIR=`/bin/ls -1d /opt/Xilinx/Vivado/* | sort | tail -1` - fi + echo "ERROR: Could not find Vivado installation." >&2 + echo "Searched in: /opt/Xilinx, /tools/Xilinx, ~/Xilinx" >&2 + echo "Set VIVADODIR environment variable to your Vivado installation path." >&2 + echo "Example: export VIVADODIR=/opt/Xilinx/Vivado/2024.1" >&2 + exit 1 +fi + +if [ ! -f "$VIVADODIR/settings64.sh" ]; then + echo "ERROR: settings64.sh not found in $VIVADODIR" >&2 + exit 1 fi -. ${VIVADODIR}/settings64.sh -echo vivado $* -vivado $* +echo "Using Vivado from: $VIVADODIR" >&2 +. "$VIVADODIR/settings64.sh" +echo vivado "$@" +vivado "$@"