diff --git a/.gitignore b/.gitignore
index 46c117d7..2fa008cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,15 +21,15 @@ hw/xilinx/MEGA65/ISE/iseconfig
hw/xilinx/MEGA65/ISE/webtalk_impact.xml
hw/xilinx/MEGA65/ISE/mega65.ipf
hw/xilinx/MEGA65/ISE/mega65_xdb
-hw/xilinx/MEGA65/Vivado/*.jou
-hw/xilinx/MEGA65/Vivado/*.str
-hw/xilinx/MEGA65/Vivado/*.cache
-hw/xilinx/MEGA65/Vivado/*.hw
-hw/xilinx/MEGA65/Vivado/*.ip_user_files
-hw/xilinx/MEGA65/Vivado/*.runs
-hw/xilinx/MEGA65/Vivado/.Xil
+hw/xilinx/MEGA65*/Vivado/*.jou
+hw/xilinx/MEGA65*/Vivado/*.str
+hw/xilinx/MEGA65*/Vivado/*.cache
+hw/xilinx/MEGA65*/Vivado/*.hw
+hw/xilinx/MEGA65*/Vivado/*.ip_user_files
+hw/xilinx/MEGA65*/Vivado/*.runs
+hw/xilinx/MEGA65*/Vivado/.Xil
+hw/xilinx/MEGA65*/Vivado/MEGA65.sim
hw/xilinx/MEGA65
-hw/xilinx/MEGA65/Vivado/MEGA65.sim
impact.xsl
impact_impact.xwbt
assembler/qasm
diff --git a/emulator/uart.h b/emulator/uart.h
index f5b3e493..6fd5122d 100644
--- a/emulator/uart.h
+++ b/emulator/uart.h
@@ -55,7 +55,7 @@ typedef struct uart
#define RESET_OUTPUT_PORT 15
//flag to ensure restoring a working terminal when closing the emulator by closing the SDL window
-enum uart_status_t {uart_undef, uart_init, uart_rundown} uart_status;
+enum uart_status_t {uart_undef, uart_init, uart_rundown};
unsigned int uart_read_register(uart *, unsigned int);
void uart_write_register(uart *, unsigned int, unsigned int);
diff --git a/hw/xilinx/MEGA65-R6/Vivado/mega65.xdc b/hw/xilinx/MEGA65-R6/Vivado/mega65.xdc
new file mode 100644
index 00000000..f114c6b9
--- /dev/null
+++ b/hw/xilinx/MEGA65-R6/Vivado/mega65.xdc
@@ -0,0 +1,405 @@
+## MiSTer2MEGA framework pin mapping
+##
+## Done by MJoergen and sy2002 in 2023 and licensed under GPL v3
+
+################################
+## Pin to signal mapping
+################################
+
+# Onboard crystal oscillator = 100 MHz
+set_property -dict {PACKAGE_PIN V13 IOSTANDARD LVCMOS33} [get_ports {clk_i}]; # CLOCK_FPGA_MRCC
+
+# Reset button on the side of the machine
+set_property -dict {PACKAGE_PIN J19 IOSTANDARD LVCMOS33} [get_ports {reset_button_i}]; # RESET
+
+# USB-RS232 Interface
+set_property -dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports {uart_rxd_i}]; # DBG_UART_RX
+set_property -dict {PACKAGE_PIN L13 IOSTANDARD LVCMOS33} [get_ports {uart_txd_o}]; # DBG_UART_TX
+
+# VGA via VDAC. U3 = ADV7125BCPZ170
+set_property -dict {PACKAGE_PIN W11 IOSTANDARD LVCMOS33} [get_ports {vdac_blank_n_o}]; # VDAC_BLANK_N
+set_property -dict {PACKAGE_PIN AA9 IOSTANDARD LVCMOS33} [get_ports {vdac_clk_o}]; # VDAC_CLK
+set_property -dict {PACKAGE_PIN W16 IOSTANDARD LVCMOS33} [get_ports {vdac_psave_n_o}]; # VDAC_PSAVE_N
+set_property -dict {PACKAGE_PIN V10 IOSTANDARD LVCMOS33} [get_ports {vdac_sync_n_o}]; # VDAC_SYNC_N
+set_property -dict {PACKAGE_PIN W10 IOSTANDARD LVCMOS33} [get_ports {vga_blue_o[0]}]; # B0
+set_property -dict {PACKAGE_PIN Y12 IOSTANDARD LVCMOS33} [get_ports {vga_blue_o[1]}]; # B1
+set_property -dict {PACKAGE_PIN AB12 IOSTANDARD LVCMOS33} [get_ports {vga_blue_o[2]}]; # B2
+set_property -dict {PACKAGE_PIN AA11 IOSTANDARD LVCMOS33} [get_ports {vga_blue_o[3]}]; # B3
+set_property -dict {PACKAGE_PIN AB11 IOSTANDARD LVCMOS33} [get_ports {vga_blue_o[4]}]; # B4
+set_property -dict {PACKAGE_PIN Y11 IOSTANDARD LVCMOS33} [get_ports {vga_blue_o[5]}]; # B5
+set_property -dict {PACKAGE_PIN AB10 IOSTANDARD LVCMOS33} [get_ports {vga_blue_o[6]}]; # B6
+set_property -dict {PACKAGE_PIN AA10 IOSTANDARD LVCMOS33} [get_ports {vga_blue_o[7]}]; # B7
+set_property -dict {PACKAGE_PIN Y14 IOSTANDARD LVCMOS33} [get_ports {vga_green_o[0]}]; # G0
+set_property -dict {PACKAGE_PIN W14 IOSTANDARD LVCMOS33} [get_ports {vga_green_o[1]}]; # G1
+set_property -dict {PACKAGE_PIN AA15 IOSTANDARD LVCMOS33} [get_ports {vga_green_o[2]}]; # G2
+set_property -dict {PACKAGE_PIN AB15 IOSTANDARD LVCMOS33} [get_ports {vga_green_o[3]}]; # G3
+set_property -dict {PACKAGE_PIN Y13 IOSTANDARD LVCMOS33} [get_ports {vga_green_o[4]}]; # G4
+set_property -dict {PACKAGE_PIN AA14 IOSTANDARD LVCMOS33} [get_ports {vga_green_o[5]}]; # G5
+set_property -dict {PACKAGE_PIN AA13 IOSTANDARD LVCMOS33} [get_ports {vga_green_o[6]}]; # G6
+set_property -dict {PACKAGE_PIN AB13 IOSTANDARD LVCMOS33} [get_ports {vga_green_o[7]}]; # G7
+set_property -dict {PACKAGE_PIN W12 IOSTANDARD LVCMOS33} [get_ports {vga_hs_o}]; # HSYNC
+set_property -dict {PACKAGE_PIN U15 IOSTANDARD LVCMOS33} [get_ports {vga_red_o[0]}]; # R0
+set_property -dict {PACKAGE_PIN V15 IOSTANDARD LVCMOS33} [get_ports {vga_red_o[1]}]; # R1
+set_property -dict {PACKAGE_PIN T14 IOSTANDARD LVCMOS33} [get_ports {vga_red_o[2]}]; # R2
+set_property -dict {PACKAGE_PIN Y17 IOSTANDARD LVCMOS33} [get_ports {vga_red_o[3]}]; # R3
+set_property -dict {PACKAGE_PIN Y16 IOSTANDARD LVCMOS33} [get_ports {vga_red_o[4]}]; # R4
+set_property -dict {PACKAGE_PIN AB17 IOSTANDARD LVCMOS33} [get_ports {vga_red_o[5]}]; # R5
+set_property -dict {PACKAGE_PIN AA16 IOSTANDARD LVCMOS33} [get_ports {vga_red_o[6]}]; # R6
+set_property -dict {PACKAGE_PIN AB16 IOSTANDARD LVCMOS33} [get_ports {vga_red_o[7]}]; # R7
+set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33} [get_ports {vga_scl_io}]; # VGA_SCL
+set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports {vga_sda_io}]; # VGA_SDA
+set_property -dict {PACKAGE_PIN V14 IOSTANDARD LVCMOS33} [get_ports {vga_vs_o}]; # VSYNC
+
+# HDMI output. U10 = PTN3363BSMP
+# I2C address 0x40.
+set_property -dict {PACKAGE_PIN Y1 IOSTANDARD TMDS_33} [get_ports {tmds_clk_n_o}]; # TXC_N
+set_property -dict {PACKAGE_PIN W1 IOSTANDARD TMDS_33} [get_ports {tmds_clk_p_o}]; # TXC_P
+set_property -dict {PACKAGE_PIN AB1 IOSTANDARD TMDS_33} [get_ports {tmds_data_n_o[0]}]; # TX0_N
+set_property -dict {PACKAGE_PIN AB2 IOSTANDARD TMDS_33} [get_ports {tmds_data_n_o[1]}]; # TX1_N
+set_property -dict {PACKAGE_PIN AB5 IOSTANDARD TMDS_33} [get_ports {tmds_data_n_o[2]}]; # TX2_N
+set_property -dict {PACKAGE_PIN AA1 IOSTANDARD TMDS_33} [get_ports {tmds_data_p_o[0]}]; # TX0_P
+set_property -dict {PACKAGE_PIN AB3 IOSTANDARD TMDS_33} [get_ports {tmds_data_p_o[1]}]; # TX1_P
+set_property -dict {PACKAGE_PIN AA5 IOSTANDARD TMDS_33} [get_ports {tmds_data_p_o[2]}]; # TX2_P
+set_property -dict {PACKAGE_PIN M15 IOSTANDARD LVCMOS33} [get_ports {hdmi_hiz_en_o}]; # HIZ_EN
+set_property -dict {PACKAGE_PIN Y8 IOSTANDARD LVCMOS33} [get_ports {hdmi_hpd_i}]; # HPD_A
+set_property -dict {PACKAGE_PIN AB7 IOSTANDARD LVCMOS33} [get_ports {hdmi_scl_io}]; # SCL_A
+set_property -dict {PACKAGE_PIN V9 IOSTANDARD LVCMOS33} [get_ports {hdmi_sda_io}]; # SDA_A
+set_property -dict {PACKAGE_PIN AB8 IOSTANDARD LVCMOS33} [get_ports {hdmi_ls_oe_n_o}]; # LS_OE
+
+# MEGA65 Keyboard
+set_property -dict {PACKAGE_PIN A14 IOSTANDARD LVCMOS33} [get_ports {kb_io0_o}]; # KB_IO1
+set_property -dict {PACKAGE_PIN A13 IOSTANDARD LVCMOS33} [get_ports {kb_io1_o}]; # KB_IO2
+set_property -dict {PACKAGE_PIN C13 IOSTANDARD LVCMOS33} [get_ports {kb_io2_i}]; # KB_IO3
+set_property -dict {PACKAGE_PIN E13 IOSTANDARD LVCMOS33} [get_ports {kb_tck_o}]; # KB_TCK
+set_property -dict {PACKAGE_PIN E14 IOSTANDARD LVCMOS33} [get_ports {kb_tdo_i}]; # KB_TDO
+set_property -dict {PACKAGE_PIN D14 IOSTANDARD LVCMOS33} [get_ports {kb_tms_o}]; # KB_TMS
+set_property -dict {PACKAGE_PIN D15 IOSTANDARD LVCMOS33} [get_ports {kb_tdi_o}]; # KB_TDI
+set_property -dict {PACKAGE_PIN B13 IOSTANDARD LVCMOS33} [get_ports {kb_jtagen_o}]; # KB_JTAGEN
+
+# Micro SD Connector (external slot at back of the cover)
+set_property -dict {PACKAGE_PIN K1 IOSTANDARD LVCMOS33} [get_ports {sd_cd_i}]; # SD_CD
+set_property -dict {PACKAGE_PIN G2 IOSTANDARD LVCMOS33} [get_ports {sd_clk_o}]; # SD_CLK
+set_property -dict {PACKAGE_PIN H2 IOSTANDARD LVCMOS33} [get_ports {sd_miso_i}]; # SD_D0
+set_property -dict {PACKAGE_PIN J2 IOSTANDARD LVCMOS33} [get_ports {sd_mosi_o}]; # SD_CMD
+set_property -dict {PACKAGE_PIN K2 IOSTANDARD LVCMOS33} [get_ports {sd_reset_o}]; # SD_D3
+set_property -dict {PACKAGE_PIN H3 IOSTANDARD LVCMOS33} [get_ports {sd_d1_i}]; # SD_D1
+set_property -dict {PACKAGE_PIN J1 IOSTANDARD LVCMOS33} [get_ports {sd_d2_i}]; # SD_D2
+
+# SD Connector (this is the slot at the bottom side of the case under the cover)
+set_property -dict {PACKAGE_PIN D17 IOSTANDARD LVCMOS33} [get_ports {sd2_cd_i}]; # SD2_CD
+set_property -dict {PACKAGE_PIN B17 IOSTANDARD LVCMOS33} [get_ports {sd2_clk_o}]; # SD2_CLK
+set_property -dict {PACKAGE_PIN B18 IOSTANDARD LVCMOS33} [get_ports {sd2_miso_i}]; # SD2_D0
+set_property -dict {PACKAGE_PIN B16 IOSTANDARD LVCMOS33} [get_ports {sd2_mosi_o}]; # SD2_CMD
+set_property -dict {PACKAGE_PIN B15 IOSTANDARD LVCMOS33} [get_ports {sd2_reset_o}]; # SD2_D3
+set_property -dict {PACKAGE_PIN C17 IOSTANDARD LVCMOS33} [get_ports {sd2_wp_i}]; # SD2_WP
+set_property -dict {PACKAGE_PIN C18 IOSTANDARD LVCMOS33} [get_ports {sd2_d1_i}]; # SD2_D1
+set_property -dict {PACKAGE_PIN C19 IOSTANDARD LVCMOS33} [get_ports {sd2_d2_i}]; # SD2_D2
+
+# Audio DAC. U37 = AK4432VT
+# I2C address: 0x19.
+set_property -dict {PACKAGE_PIN D16 IOSTANDARD LVCMOS33} [get_ports {audio_mclk_o}]; # AUDIO_MCLK
+set_property -dict {PACKAGE_PIN E19 IOSTANDARD LVCMOS33} [get_ports {audio_bick_o}]; # AUDIO_BCLK
+set_property -dict {PACKAGE_PIN E16 IOSTANDARD LVCMOS33} [get_ports {audio_sdti_o}]; # AUDIO_SDATA
+set_property -dict {PACKAGE_PIN F19 IOSTANDARD LVCMOS33} [get_ports {audio_lrclk_o}]; # AUDIO_LRCLK
+set_property -dict {PACKAGE_PIN F18 IOSTANDARD LVCMOS33} [get_ports {audio_pdn_n_o}]; # nSD_AUDIO
+set_property -dict {PACKAGE_PIN F4 IOSTANDARD LVCMOS33} [get_ports {audio_i2cfil_o}]; # AUDIO1
+set_property -dict {PACKAGE_PIN L6 IOSTANDARD LVCMOS33} [get_ports {audio_scl_io}]; # AUDIO2
+set_property -dict {PACKAGE_PIN W9 IOSTANDARD LVCMOS33} [get_ports {audio_sda_io}]; # AUDIO3
+
+# Joystick
+set_property -dict {PACKAGE_PIN F16 IOSTANDARD LVCMOS33} [get_ports {fa_down_n_i}]; # FA_DOWN
+set_property -dict {PACKAGE_PIN E17 IOSTANDARD LVCMOS33} [get_ports {fa_fire_n_i}]; # FA_FIRE
+set_property -dict {PACKAGE_PIN F14 IOSTANDARD LVCMOS33} [get_ports {fa_left_n_i}]; # FA_LEFT
+set_property -dict {PACKAGE_PIN F13 IOSTANDARD LVCMOS33} [get_ports {fa_right_n_i}]; # FA_RIGHT
+set_property -dict {PACKAGE_PIN C14 IOSTANDARD LVCMOS33} [get_ports {fa_up_n_i}]; # FA_UP
+set_property -dict {PACKAGE_PIN P17 IOSTANDARD LVCMOS33} [get_ports {fb_down_n_i}]; # FB_DOWN
+set_property -dict {PACKAGE_PIN F15 IOSTANDARD LVCMOS33} [get_ports {fb_fire_n_i}]; # FB_FIRE
+set_property -dict {PACKAGE_PIN F21 IOSTANDARD LVCMOS33} [get_ports {fb_left_n_i}]; # FB_LEFT
+set_property -dict {PACKAGE_PIN C15 IOSTANDARD LVCMOS33} [get_ports {fb_right_n_i}]; # FB_RIGHT
+set_property -dict {PACKAGE_PIN W19 IOSTANDARD LVCMOS33} [get_ports {fb_up_n_i}]; # FB_UP
+
+# Paddles
+set_property -dict {PACKAGE_PIN H22 IOSTANDARD LVCMOS33} [get_ports {paddle_drain_o}]; # Pulse-discharge
+set_property -dict {PACKAGE_PIN H13 IOSTANDARD LVCMOS33} [get_ports {paddle_i[0]}]; # CP0
+set_property -dict {PACKAGE_PIN G15 IOSTANDARD LVCMOS33} [get_ports {paddle_i[1]}]; # CP1
+set_property -dict {PACKAGE_PIN J14 IOSTANDARD LVCMOS33} [get_ports {paddle_i[2]}]; # CP2
+set_property -dict {PACKAGE_PIN J22 IOSTANDARD LVCMOS33} [get_ports {paddle_i[3]}]; # CP3
+
+# HyperRAM. U29 = IS66WVH8M8DBLL-100B1LI
+set_property -dict {PACKAGE_PIN D22 IOSTANDARD LVCMOS33} [get_ports {hr_clk_p_o}]; # H_CLK
+set_property -dict {PACKAGE_PIN C22 IOSTANDARD LVCMOS33} [get_ports {hr_cs0_o}]; # CS0
+set_property -dict {PACKAGE_PIN A21 IOSTANDARD LVCMOS33} [get_ports {hr_d_io[0]}]; # DQ0
+set_property -dict {PACKAGE_PIN D21 IOSTANDARD LVCMOS33} [get_ports {hr_d_io[1]}]; # DQ1
+set_property -dict {PACKAGE_PIN C20 IOSTANDARD LVCMOS33} [get_ports {hr_d_io[2]}]; # DQ2
+set_property -dict {PACKAGE_PIN A20 IOSTANDARD LVCMOS33} [get_ports {hr_d_io[3]}]; # DQ3
+set_property -dict {PACKAGE_PIN B20 IOSTANDARD LVCMOS33} [get_ports {hr_d_io[4]}]; # DQ4
+set_property -dict {PACKAGE_PIN A19 IOSTANDARD LVCMOS33} [get_ports {hr_d_io[5]}]; # DQ5
+set_property -dict {PACKAGE_PIN E21 IOSTANDARD LVCMOS33} [get_ports {hr_d_io[6]}]; # DQ6
+set_property -dict {PACKAGE_PIN E22 IOSTANDARD LVCMOS33} [get_ports {hr_d_io[7]}]; # DQ7
+set_property -dict {PACKAGE_PIN B22 IOSTANDARD LVCMOS33} [get_ports {hr_reset_o}]; # H_RES
+set_property -dict {PACKAGE_PIN B21 IOSTANDARD LVCMOS33} [get_ports {hr_rwds_io}]; # RWDS
+set_property -dict {PULLTYPE {} SLEW FAST DRIVE 16} [get_ports {hr_reset_o}];
+set_property -dict {PULLTYPE {} SLEW FAST DRIVE 16} [get_ports {hr_cs0_o}];
+set_property -dict {PULLTYPE {} SLEW FAST DRIVE 16} [get_ports {hr_clk_p_o}];
+set_property -dict {PULLTYPE {} SLEW FAST DRIVE 16} [get_ports {hr_d_io[*]}];
+set_property -dict {PULLTYPE PULLDOWN SLEW FAST DRIVE 16} [get_ports {hr_rwds_io}];
+
+# CBM-488/IEC serial port
+set_property -dict {PACKAGE_PIN N17 IOSTANDARD LVCMOS33} [get_ports {iec_atn_n_o}]; # F_SER_ATN
+set_property -dict {PACKAGE_PIN AA21 IOSTANDARD LVCMOS33} [get_ports {iec_clk_en_n_o}]; # F_SER_CLK_EN. Active low
+set_property -dict {PACKAGE_PIN Y18 IOSTANDARD LVCMOS33} [get_ports {iec_clk_n_i}]; # F_SER_CLK_I
+set_property -dict {PACKAGE_PIN Y19 IOSTANDARD LVCMOS33} [get_ports {iec_clk_n_o}]; # F_SER_CLK_O
+set_property -dict {PACKAGE_PIN Y21 IOSTANDARD LVCMOS33} [get_ports {iec_data_en_n_o}]; # F_SER_DATA_EN. Active low
+set_property -dict {PACKAGE_PIN AB22 IOSTANDARD LVCMOS33} [get_ports {iec_data_n_i}]; # F_SER_DATA_I
+set_property -dict {PACKAGE_PIN Y22 IOSTANDARD LVCMOS33} [get_ports {iec_data_n_o}]; # F_SER_DATA_O
+set_property -dict {PACKAGE_PIN AB21 IOSTANDARD LVCMOS33} [get_ports {iec_reset_n_o}]; # F_SER_RESET
+set_property -dict {PACKAGE_PIN AB20 IOSTANDARD LVCMOS33} [get_ports {iec_srq_en_n_o}]; # F_SER_SRQ_EN. Active low
+set_property -dict {PACKAGE_PIN AA18 IOSTANDARD LVCMOS33} [get_ports {iec_srq_n_i}]; # F_SER_SRQ_I
+set_property -dict {PACKAGE_PIN U20 IOSTANDARD LVCMOS33} [get_ports {iec_srq_n_o}]; # F_SER_SRQ_O
+set_property -dict {PULLUP TRUE} [get_ports {iec_clk_n_i}];
+set_property -dict {PULLUP TRUE} [get_ports {iec_data_n_i}];
+
+# C64 Expansion Port (aka Cartridge Port)
+set_property -dict {PACKAGE_PIN V17 IOSTANDARD LVCMOS33} [get_ports {cart_phi2_o}]; # F_C64_O2
+set_property -dict {PACKAGE_PIN AA19 IOSTANDARD LVCMOS33} [get_ports {cart_dotclock_o}]; # F_C64_CLOCK
+set_property -dict {PACKAGE_PIN P15 IOSTANDARD LVCMOS33} [get_ports {cart_dma_i}]; # F_C64_DMA
+set_property -dict {PACKAGE_PIN T20 IOSTANDARD LVCMOS33} [get_ports {cart_reset_oe_n_o}]; # F_C64_RESET_EN
+set_property -dict {PACKAGE_PIN N14 IOSTANDARD LVCMOS33} [get_ports {cart_reset_io}]; # F_C64_RESET
+set_property -dict {PACKAGE_PIN L15 IOSTANDARD LVCMOS33} [get_ports {cart_game_oe_n_o}]; # F_C64_GAME_EN
+set_property -dict {PACKAGE_PIN W22 IOSTANDARD LVCMOS33} [get_ports {cart_game_io}]; #_F_C64_GAME
+set_property -dict {PACKAGE_PIN M16 IOSTANDARD LVCMOS33} [get_ports {cart_exrom_oe_n_o}]; # F_C64_EXROM_EN
+set_property -dict {PACKAGE_PIN R19 IOSTANDARD LVCMOS33} [get_ports {cart_exrom_io}]; # F_C64_EXROM
+set_property -dict {PACKAGE_PIN F20 IOSTANDARD LVCMOS33} [get_ports {cart_nmi_oe_n_o}]; # F_C64_NMI_EN
+set_property -dict {PACKAGE_PIN W17 IOSTANDARD LVCMOS33} [get_ports {cart_nmi_io}]; # F_C64_NMI
+set_property -dict {PACKAGE_PIN H14 IOSTANDARD LVCMOS33} [get_ports {cart_irq_oe_n_o}]; # F_C64_IRQ_EN
+set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVCMOS33} [get_ports {cart_irq_io}]; # F_C64_IRQ
+set_property -dict {PACKAGE_PIN G18 IOSTANDARD LVCMOS33} [get_ports {cart_ctrl_en_o}]; # F_CTRL_EN
+set_property -dict {PACKAGE_PIN U17 IOSTANDARD LVCMOS33} [get_ports {cart_ctrl_dir_o}]; # F_CTRL_DIR
+set_property -dict {PACKAGE_PIN N13 IOSTANDARD LVCMOS33} [get_ports {cart_ba_io}]; # F_C64_BA
+set_property -dict {PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [get_ports {cart_rw_io}]; # F_C64_RW
+set_property -dict {PACKAGE_PIN N15 IOSTANDARD LVCMOS33} [get_ports {cart_io1_io}]; # F_C64_IO1
+set_property -dict {PACKAGE_PIN AA20 IOSTANDARD LVCMOS33} [get_ports {cart_io2_io}]; # F_C64_IO2
+set_property -dict {PACKAGE_PIN T16 IOSTANDARD LVCMOS33} [get_ports {cart_romh_oe_n_o}]; # F_C64_ROMH_DIR
+set_property -dict {PACKAGE_PIN T18 IOSTANDARD LVCMOS33} [get_ports {cart_romh_io}]; # F_C64_ROMH
+set_property -dict {PACKAGE_PIN U16 IOSTANDARD LVCMOS33} [get_ports {cart_roml_oe_n_o}]; # F_C64_ROML_DIR
+set_property -dict {PACKAGE_PIN AB18 IOSTANDARD LVCMOS33} [get_ports {cart_roml_io}]; # F_C64_ROML
+set_property -dict {PACKAGE_PIN T21 IOSTANDARD LVCMOS33} [get_ports {cart_en_o}]; # EXP_SLOT_EN
+set_property -dict {PACKAGE_PIN L19 IOSTANDARD LVCMOS33} [get_ports {cart_addr_en_o}]; # F_ADDR_EN
+set_property -dict {PACKAGE_PIN L18 IOSTANDARD LVCMOS33} [get_ports {cart_haddr_dir_o}]; # F_HADDR_DIR
+set_property -dict {PACKAGE_PIN L21 IOSTANDARD LVCMOS33} [get_ports {cart_laddr_dir_o}]; # F_LADDR_DIR
+set_property -dict {PACKAGE_PIN K19 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[0]}]; # F_C64_A0
+set_property -dict {PACKAGE_PIN K18 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[1]}]; # F_C64_A1
+set_property -dict {PACKAGE_PIN K21 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[2]}]; # F_C64_A2
+set_property -dict {PACKAGE_PIN M22 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[3]}]; # F_C64_A3
+set_property -dict {PACKAGE_PIN L20 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[4]}]; # F_C64_A4
+set_property -dict {PACKAGE_PIN J20 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[5]}]; # F_C64_A5
+set_property -dict {PACKAGE_PIN J21 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[6]}]; # F_C64_A6
+set_property -dict {PACKAGE_PIN K22 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[7]}]; # F_C64_A7
+set_property -dict {PACKAGE_PIN H17 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[8]}]; # F_C64_A8
+set_property -dict {PACKAGE_PIN H20 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[9]}]; # F_C64_A9
+set_property -dict {PACKAGE_PIN G20 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[10]}]; # F_C64_A10
+set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[11]}]; # F_C64_A11
+set_property -dict {PACKAGE_PIN H19 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[12]}]; # F_C64_A12
+set_property -dict {PACKAGE_PIN M20 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[13]}]; # F_C64_A13
+set_property -dict {PACKAGE_PIN N22 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[14]}]; # F_C64_A14
+set_property -dict {PACKAGE_PIN H18 IOSTANDARD LVCMOS33} [get_ports {cart_a_io[15]}]; # F_C64_A15
+set_property -dict {PACKAGE_PIN U21 IOSTANDARD LVCMOS33} [get_ports {cart_data_en_o}]; # F_DATA_EN
+set_property -dict {PACKAGE_PIN V22 IOSTANDARD LVCMOS33} [get_ports {cart_data_dir_o}]; # F_DATA_DIR
+set_property -dict {PACKAGE_PIN P16 IOSTANDARD LVCMOS33} [get_ports {cart_d_io[0]}]; # F_C64_D0
+set_property -dict {PACKAGE_PIN R17 IOSTANDARD LVCMOS33} [get_ports {cart_d_io[1]}]; # F_C64_D1
+set_property -dict {PACKAGE_PIN P20 IOSTANDARD LVCMOS33} [get_ports {cart_d_io[2]}]; # F_C64_D2
+set_property -dict {PACKAGE_PIN R16 IOSTANDARD LVCMOS33} [get_ports {cart_d_io[3]}]; # F_C64_D3
+set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports {cart_d_io[4]}]; # F_C64_D4
+set_property -dict {PACKAGE_PIN V18 IOSTANDARD LVCMOS33} [get_ports {cart_d_io[5]}]; # F_C64_D5
+set_property -dict {PACKAGE_PIN W20 IOSTANDARD LVCMOS33} [get_ports {cart_d_io[6]}]; # F_C64_D6
+set_property -dict {PACKAGE_PIN W21 IOSTANDARD LVCMOS33} [get_ports {cart_d_io[7]}]; # F_C64_D7
+
+# I2C bus
+# U32 = PCA9655EMTTXG. Address 0x40. I/O expander.
+# U12 = MP8869SGL-Z. Address 0x61. DC/DC Converter.
+# U14 = MP8869SGL-Z. Address 0x67. DC/DC Converter.
+set_property -dict {PACKAGE_PIN N18 IOSTANDARD LVCMOS33} [get_ports {i2c_scl_io}]; # I2C_SCL
+set_property -dict {PACKAGE_PIN P19 IOSTANDARD LVCMOS33} [get_ports {i2c_sda_io}]; # I2C_SDA
+
+# Debug. Also used to control output to joystick ??
+set_property -dict {PACKAGE_PIN J17 IOSTANDARD LVCMOS33} [get_ports {fa_fire_n_o}]; # DBG0 = FA_FIRE_O
+set_property -dict {PACKAGE_PIN G16 IOSTANDARD LVCMOS33} [get_ports {fa_up_n_o}]; # DBG1 = FA_UP_O
+set_property -dict {PACKAGE_PIN K13 IOSTANDARD LVCMOS33} [get_ports {fa_left_n_o}]; # DBG2 = FA_LEFT_O
+set_property -dict {PACKAGE_PIN K14 IOSTANDARD LVCMOS33} [get_ports {fa_down_n_o}]; # DBG3 = FA_DOWN_O
+set_property -dict {PACKAGE_PIN N20 IOSTANDARD LVCMOS33} [get_ports {fb_up_n_o}]; # DBG4 = FB_UP_O
+set_property -dict {PACKAGE_PIN L16 IOSTANDARD LVCMOS33} [get_ports {fa_right_n_o}]; # DBG5 = FA_RIGHT_O
+set_property -dict {PACKAGE_PIN M18 IOSTANDARD LVCMOS33} [get_ports {fb_down_n_o}]; # DBG6 = FB_DOWN_O
+set_property -dict {PACKAGE_PIN N19 IOSTANDARD LVCMOS33} [get_ports {fb_fire_n_o}]; # DBG7 = FB_FIRE_O
+set_property -dict {PACKAGE_PIN E18 IOSTANDARD LVCMOS33} [get_ports {fb_right_n_o}]; # DBG8 = FB_RIGHT_O
+set_property -dict {PACKAGE_PIN M17 IOSTANDARD LVCMOS33} [get_ports {fb_left_n_o}]; # DBG9 = FB_LEFT_O
+set_property -dict {PACKAGE_PIN G13 IOSTANDARD LVCMOS33} [get_ports {dbg_11_io}]; # DBG11
+
+# SMSC Ethernet PHY. U4 = KSZ8081RNDCA
+set_property -dict {PACKAGE_PIN L4 IOSTANDARD LVCMOS33} [get_ports {eth_clock_o}]; # ETH_CLK
+set_property -dict {PACKAGE_PIN R14 IOSTANDARD LVCMOS33} [get_ports {eth_led2_o}]; # ETH_LED2
+set_property -dict {PACKAGE_PIN J6 IOSTANDARD LVCMOS33} [get_ports {eth_mdc_o}]; # ETH_MDC
+set_property -dict {PACKAGE_PIN L5 IOSTANDARD LVCMOS33} [get_ports {eth_mdio_io}]; # ETH_MDIO
+set_property -dict {PACKAGE_PIN K6 IOSTANDARD LVCMOS33} [get_ports {eth_reset_o}]; # ETH-RST
+set_property -dict {PACKAGE_PIN P4 IOSTANDARD LVCMOS33} [get_ports {eth_rxd_i[0]}]; # ETH_RX_D0
+set_property -dict {PACKAGE_PIN L1 IOSTANDARD LVCMOS33} [get_ports {eth_rxd_i[1]}]; # ETH_RX_D1
+set_property -dict {PACKAGE_PIN K4 IOSTANDARD LVCMOS33} [get_ports {eth_rxdv_i}]; # ETH_CRS_DV
+set_property -dict {PACKAGE_PIN M6 IOSTANDARD LVCMOS33} [get_ports {eth_rxer_i}]; # ETH_RXER
+set_property -dict {PACKAGE_PIN L3 IOSTANDARD LVCMOS33} [get_ports {eth_txd_o[0]}]; # ETH_TX_D0
+set_property -dict {PACKAGE_PIN K3 IOSTANDARD LVCMOS33} [get_ports {eth_txd_o[1]}]; # ETH_TX_D1
+set_property -dict {PACKAGE_PIN J4 IOSTANDARD LVCMOS33} [get_ports {eth_txen_o}]; # ETH_TX_EN
+set_property -dict {SLEW SLOW DRIVE 4} [get_ports {eth_txd_o[*]}];
+set_property -dict {SLEW SLOW DRIVE 4} [get_ports {eth_txen_o}];
+set_property -dict {SLEW FAST} [get_ports {eth_clock_o}];
+
+# FDC interface
+set_property -dict {PACKAGE_PIN P6 IOSTANDARD LVCMOS33} [get_ports {f_density_o}]; # F_REDWC
+set_property -dict {PACKAGE_PIN R1 IOSTANDARD LVCMOS33} [get_ports {f_diskchanged_i}]; # F_DSCKCHG
+set_property -dict {PACKAGE_PIN M2 IOSTANDARD LVCMOS33} [get_ports {f_index_i}]; # F_INDEX
+set_property -dict {PACKAGE_PIN M5 IOSTANDARD LVCMOS33} [get_ports {f_motora_o}]; # F_MOTEA
+set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports {f_motorb_o}]; # F_MOTEB
+set_property -dict {PACKAGE_PIN P1 IOSTANDARD LVCMOS33} [get_ports {f_rdata_i}]; # F_RDATA1
+set_property -dict {PACKAGE_PIN N5 IOSTANDARD LVCMOS33} [get_ports {f_selecta_o}]; # F_DRVSA
+set_property -dict {PACKAGE_PIN G17 IOSTANDARD LVCMOS33} [get_ports {f_selectb_o}]; # F_DRVSB
+set_property -dict {PACKAGE_PIN M1 IOSTANDARD LVCMOS33} [get_ports {f_side1_o}]; # F_SIDE1
+set_property -dict {PACKAGE_PIN P5 IOSTANDARD LVCMOS33} [get_ports {f_stepdir_o}]; # F_DIR
+set_property -dict {PACKAGE_PIN M3 IOSTANDARD LVCMOS33} [get_ports {f_step_o}]; # F_STEP
+set_property -dict {PACKAGE_PIN N2 IOSTANDARD LVCMOS33} [get_ports {f_track0_i}]; # F_TRCK0
+set_property -dict {PACKAGE_PIN N4 IOSTANDARD LVCMOS33} [get_ports {f_wdata_o}]; # F_WDATE
+set_property -dict {PACKAGE_PIN N3 IOSTANDARD LVCMOS33} [get_ports {f_wgate_o}]; # F_WGATE
+set_property -dict {PACKAGE_PIN P2 IOSTANDARD LVCMOS33} [get_ports {f_writeprotect_i}]; # F_WPT
+
+# I2C bus for on-board peripherals
+# U36. 24AA025E48T. Address 0x50. 2K Serial EEPROM.
+# U38. RV-3032-C7. Address 0x51. Real-Time Clock Module.
+# U39. 24LC128. Address 0x56. 128K CMOS Serial EEPROM.
+set_property -dict {PACKAGE_PIN A15 IOSTANDARD LVCMOS33} [get_ports {fpga_scl_io}]; # FPGA_SCL
+set_property -dict {PACKAGE_PIN A16 IOSTANDARD LVCMOS33} [get_ports {fpga_sda_io}]; # FPGA_SDA
+
+# Connected to J18.
+set_property -dict {PACKAGE_PIN G21 IOSTANDARD LVCMOS33} [get_ports {grove_scl_io}]; # Grove_SCL0
+set_property -dict {PACKAGE_PIN G22 IOSTANDARD LVCMOS33} [get_ports {grove_sda_io}]; # Grove_SDA0
+
+# Joystick power supply
+set_property -dict {PACKAGE_PIN D19 IOSTANDARD LVCMOS33} [get_ports {joystick_5v_disable_o}]; # EN_5V_JOY_N
+set_property -dict {PACKAGE_PIN D20 IOSTANDARD LVCMOS33} [get_ports {joystick_5v_powergood_i}]; # 5V_JOY_PG
+
+# On board LEDs
+set_property -dict {PACKAGE_PIN V19 IOSTANDARD LVCMOS33} [get_ports {led_g_n_o}]; # LED_G
+set_property -dict {PACKAGE_PIN U22 IOSTANDARD LVCMOS33} [get_ports {led_o}]; # ULED
+set_property -dict {PACKAGE_PIN V20 IOSTANDARD LVCMOS33} [get_ports {led_r_n_o}]; # LED_R
+
+# Pmod Header
+set_property -dict {PACKAGE_PIN A18 IOSTANDARD LVCMOS33} [get_ports {p1hi_io[0]}]; # B16_L17_P
+set_property -dict {PACKAGE_PIN E1 IOSTANDARD LVCMOS33} [get_ports {p1hi_io[1]}]; # B35_L3_P
+set_property -dict {PACKAGE_PIN C2 IOSTANDARD LVCMOS33} [get_ports {p1hi_io[2]}]; # B35_L2_P
+set_property -dict {PACKAGE_PIN B1 IOSTANDARD LVCMOS33} [get_ports {p1hi_io[3]}]; # B35_L1_P
+set_property -dict {PACKAGE_PIN F1 IOSTANDARD LVCMOS33} [get_ports {p1lo_io[0]}]; # B35_L5_N
+set_property -dict {PACKAGE_PIN D1 IOSTANDARD LVCMOS33} [get_ports {p1lo_io[1]}]; # B35_L3_N
+set_property -dict {PACKAGE_PIN B2 IOSTANDARD LVCMOS33} [get_ports {p1lo_io[2]}]; # B35_L2_N
+set_property -dict {PACKAGE_PIN A1 IOSTANDARD LVCMOS33} [get_ports {p1lo_io[3]}]; # B35_L1_N
+set_property -dict {PACKAGE_PIN E2 IOSTANDARD LVCMOS33} [get_ports {p2hi_io[0]}]; # B35_L4_P
+set_property -dict {PACKAGE_PIN D2 IOSTANDARD LVCMOS33} [get_ports {p2hi_io[1]}]; # B35_L4_N
+set_property -dict {PACKAGE_PIN G4 IOSTANDARD LVCMOS33} [get_ports {p2hi_io[2]}]; # B35_L12_N
+set_property -dict {PACKAGE_PIN J5 IOSTANDARD LVCMOS33} [get_ports {p2hi_io[3]}]; # B35_L10_P
+set_property -dict {PACKAGE_PIN F3 IOSTANDARD LVCMOS33} [get_ports {p2lo_io[0]}]; # B35_L6_P
+set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports {p2lo_io[1]}]; # B35_L6_N
+set_property -dict {PACKAGE_PIN H4 IOSTANDARD LVCMOS33} [get_ports {p2lo_io[2]}]; # B35_L12_P
+set_property -dict {PACKAGE_PIN H5 IOSTANDARD LVCMOS33} [get_ports {p2lo_io[3]}]; # B35_L10_N
+set_property -dict {PACKAGE_PIN J16 IOSTANDARD LVCMOS33} [get_ports {pmod1_en_o}]; # PMOD1_EN
+set_property -dict {PACKAGE_PIN K16 IOSTANDARD LVCMOS33} [get_ports {pmod1_flag_i}]; # PMOD1_FLG
+set_property -dict {PACKAGE_PIN M13 IOSTANDARD LVCMOS33} [get_ports {pmod2_en_o}]; # PMOD2_EN
+set_property -dict {PACKAGE_PIN K17 IOSTANDARD LVCMOS33} [get_ports {pmod2_flag_i}]; # PMOD2_FLG
+
+# Quad SPI Flash. U5 = S25FL512SAGBHIS10
+set_property -dict {PACKAGE_PIN T19 IOSTANDARD LVCMOS33} [get_ports {qspicsn_o}]; # SPI-CS
+set_property -dict {PACKAGE_PIN P22 IOSTANDARD LVCMOS33} [get_ports {qspidb_io[0]}]; # SPI-DQ0
+set_property -dict {PACKAGE_PIN R22 IOSTANDARD LVCMOS33} [get_ports {qspidb_io[1]}]; # SPI-DQ1
+set_property -dict {PACKAGE_PIN P21 IOSTANDARD LVCMOS33} [get_ports {qspidb_io[2]}]; # SPI-DQ2
+set_property -dict {PACKAGE_PIN R21 IOSTANDARD LVCMOS33} [get_ports {qspidb_io[3]}]; # SPI-DQ3
+set_property -dict {PULLUP TRUE} [get_ports {qspidb_io[*]}];
+
+# SDRAM - 32M x 16 bit, 3.3V VCC. U44 = IS42S16320F-6BL
+set_property -dict {PACKAGE_PIN T4 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[0]}]; # SDRAM_A0
+set_property -dict {PACKAGE_PIN R2 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[1]}]; # SDRAM_A1
+set_property -dict {PACKAGE_PIN R3 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[2]}]; # SDRAM_A2
+set_property -dict {PACKAGE_PIN T3 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[3]}]; # SDRAM_A3
+set_property -dict {PACKAGE_PIN Y4 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[4]}]; # SDRAM_A4
+set_property -dict {PACKAGE_PIN W6 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[5]}]; # SDRAM_A5
+set_property -dict {PACKAGE_PIN W4 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[6]}]; # SDRAM_A6
+set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[7]}]; # SDRAM_A7
+set_property -dict {PACKAGE_PIN AA8 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[8]}]; # SDRAM_A8
+set_property -dict {PACKAGE_PIN Y2 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[9]}]; # SDRAM_A9
+set_property -dict {PACKAGE_PIN R6 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[10]}]; # SDRAM_A10
+set_property -dict {PACKAGE_PIN Y7 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[11]}]; # SDRAM_A11
+set_property -dict {PACKAGE_PIN Y9 IOSTANDARD LVCMOS33} [get_ports {sdram_a_o[12]}]; # SDRAM_A12
+set_property -dict {PACKAGE_PIN U3 IOSTANDARD LVCMOS33} [get_ports {sdram_ba_o[0]}]; # SDRAM_BA0
+set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS33} [get_ports {sdram_ba_o[1]}]; # SDRAM_BA1
+set_property -dict {PACKAGE_PIN V3 IOSTANDARD LVCMOS33} [get_ports {sdram_cas_n_o}]; # SDRAM_CAS#
+set_property -dict {PACKAGE_PIN U5 IOSTANDARD LVCMOS33} [get_ports {sdram_cke_o}]; # SDRAM_CKE
+set_property -dict {PACKAGE_PIN V8 IOSTANDARD LVCMOS33} [get_ports {sdram_clk_o}]; # SDRAM_CLK
+set_property -dict {PACKAGE_PIN G3 IOSTANDARD LVCMOS33} [get_ports {sdram_cs_n_o}]; # SDRAM_CS#
+set_property -dict {PACKAGE_PIN V5 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[0]}]; # SDRAM_DQ0
+set_property -dict {PACKAGE_PIN AA4 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[10]}]; # SDRAM_DQ10
+set_property -dict {PACKAGE_PIN V7 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[11]}]; # SDRAM_DQ11
+set_property -dict {PACKAGE_PIN AA6 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[12]}]; # SDRAM_DQ12
+set_property -dict {PACKAGE_PIN W5 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[13]}]; # SDRAM_DQ13
+set_property -dict {PACKAGE_PIN AB6 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[14]}]; # SDRAM_DQ14
+set_property -dict {PACKAGE_PIN Y3 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[15]}]; # SDRAM_DQ15
+set_property -dict {PACKAGE_PIN T1 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[1]}]; # SDRAM_DQ1
+set_property -dict {PACKAGE_PIN V4 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[2]}]; # SDRAM_DQ2
+set_property -dict {PACKAGE_PIN U2 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[3]}]; # SDRAM_DQ3
+set_property -dict {PACKAGE_PIN V2 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[4]}]; # SDRAM_DQ4
+set_property -dict {PACKAGE_PIN U1 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[5]}]; # SDRAM_DQ5
+set_property -dict {PACKAGE_PIN U6 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[6]}]; # SDRAM_DQ6
+set_property -dict {PACKAGE_PIN T6 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[7]}]; # SDRAM_DQ7
+set_property -dict {PACKAGE_PIN W7 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[8]}]; # SDRAM_DQ8
+set_property -dict {PACKAGE_PIN AA3 IOSTANDARD LVCMOS33} [get_ports {sdram_dq_io[9]}]; # SDRAM_DQ9
+set_property -dict {PACKAGE_PIN Y6 IOSTANDARD LVCMOS33} [get_ports {sdram_dqmh_o}]; # SDRAM_DQMH
+set_property -dict {PACKAGE_PIN W2 IOSTANDARD LVCMOS33} [get_ports {sdram_dqml_o}]; # SDRAM_DQML
+set_property -dict {PACKAGE_PIN T5 IOSTANDARD LVCMOS33} [get_ports {sdram_ras_n_o}]; # SDRAM_RAS#
+set_property -dict {PACKAGE_PIN G1 IOSTANDARD LVCMOS33} [get_ports {sdram_we_n_o}]; # SDRAM_WE#
+set_property -dict {PULLUP FALSE SLEW FAST DRIVE 16} [get_ports {sdram_*}];
+
+
+################################
+## TIMING CONSTRAINTS
+################################
+
+## External clock signal (100 MHz)
+create_clock -period 10.000 [get_ports clk_i]
+
+create_generated_clock -name Slow_Clock_25MHz -source [get_pins {clk_main/CLKOUT2}] -divide_by 2 [get_pins sd_card/Slow_Clock_25MHz_reg/Q]
+
+## Make the general clocks and the pixelclock unrelated to other to avoid erroneous timing
+## violations, and hopefully make everything synthesise faster
+set_clock_groups -asynchronous \
+ -group { clk_i clk_100MHz clk_200MHz clkfbin clk_50MHz } \
+ -group [get_clocks -of_objects [get_pins clk_main/CLKOUT0]]
+
+## EAE's combinatorial division networks take longer than
+## the regular clock period, so we specify a multicycle path
+## see also the comments in EAE.vhd and explanations in UG903/chapter 5/Multicycle Paths as well as ug911/page 25
+set_multicycle_path -from [get_cells -include_replicated {{eae_inst/op0_reg[*]} {eae_inst/op1_reg[*]}}] -to [get_cells -include_replicated {eae_inst/res_reg[*]}] -setup 3
+set_multicycle_path -from [get_cells -include_replicated {{eae_inst/op0_reg[*]} {eae_inst/op1_reg[*]}}] -to [get_cells -include_replicated {eae_inst/res_reg[*]}] -hold 2
+
+## The following set_max delay works fine, too at 50 MHz main clock and is an alternative to the multicycle path
+#set_max_delay -from [get_cells {{eae_inst/op0_reg[*]} {eae_inst/op1_reg[*]}}] -to [get_cells {eae_inst/res_reg[*]}] 34.000
+
+################################
+## PLACEMENT CONSTRAINTS
+################################
+
+# Place Keyboard close to I/O pins
+#create_pblock pblock_m65driver
+#add_cells_to_pblock pblock_m65driver [get_cells [list framework_inst/m2m_keyb_inst/mega65kbd_to_matrix_inst]]
+#resize_pblock pblock_m65driver -add {SLICE_X0Y225:SLICE_X7Y243}
+
+# Place SD card controller in the middle between the left and right FPGA boundary because the output ports are at the opposide edges
+create_pblock pblock_sdcard
+add_cells_to_pblock pblock_sdcard [get_cells [list sd_card]]
+resize_pblock pblock_sdcard -add {SLICE_X66Y178:SLICE_X99Y193}
+
+# Place phase-shifted VGA output registers near the actual output buffers
+#create_pblock pblock_vga
+#add_cells_to_pblock pblock_vga [get_cells [list framework_inst/av_pipeline_inst/analog_pipeline_inst/VGA_OUT_PHASE_SHIFTED.*]]
+#resize_pblock pblock_vga -add SLICE_X0Y75:SLICE_X5Y99
\ No newline at end of file
diff --git a/hw/xilinx/MEGA65-R6/Vivado/mega65.xpr b/hw/xilinx/MEGA65-R6/Vivado/mega65.xpr
new file mode 100644
index 00000000..92f1301b
--- /dev/null
+++ b/hw/xilinx/MEGA65-R6/Vivado/mega65.xpr
@@ -0,0 +1,526 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ default_dashboard
+
+
+
diff --git a/vhdl/hw/MEGA65/MEGA65_R6.vhd b/vhdl/hw/MEGA65/MEGA65_R6.vhd
new file mode 100644
index 00000000..a538f50d
--- /dev/null
+++ b/vhdl/hw/MEGA65/MEGA65_R6.vhd
@@ -0,0 +1,881 @@
+----------------------------------------------------------------------------------
+-- MEGA65 port of QNICE-FGA
+--
+-- Top Module for synthesizing the whole machine
+--
+-- done on-again-off-again in 2015, 2016 by sy2002
+-- MEGA65 port done in April to August 2020 by sy2002
+----------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+use work.types_pkg.all;
+use work.video_modes_pkg.all;
+use work.env1_globals.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+library xpm;
+use xpm.vcomponents.all;
+
+entity MEGA65 is
+ port (
+ clk_i : in std_logic; -- Onboard crystal oscillator = 100 MHz
+ reset_button_i : in std_logic; -- Reset button on the side of the machine, active high
+
+ -- USB-RS232 Interface
+ uart_rxd_i : in std_logic;
+ uart_txd_o : out std_logic;
+
+ -- VGA via VDAC. U3 = ADV7125BCPZ170
+ vga_red_o : out std_logic_vector(7 downto 0);
+ vga_green_o : out std_logic_vector(7 downto 0);
+ vga_blue_o : out std_logic_vector(7 downto 0);
+ vga_hs_o : out std_logic;
+ vga_vs_o : out std_logic;
+ vga_scl_io : inout std_logic;
+ vga_sda_io : inout std_logic;
+ vdac_clk_o : out std_logic;
+ vdac_sync_n_o : out std_logic;
+ vdac_blank_n_o : out std_logic;
+ vdac_psave_n_o : out std_logic;
+
+ -- HDMI. U10 = PTN3363BSMP
+ -- I2C address 0x40
+
+ tmds_data_p_o : out std_logic_vector(2 downto 0);
+ tmds_data_n_o : out std_logic_vector(2 downto 0);
+ tmds_clk_p_o : out std_logic;
+ tmds_clk_n_o : out std_logic;
+ hdmi_hiz_en_o : out std_logic; -- Connect to U10.HIZ_EN
+ hdmi_ls_oe_n_o : out std_logic; -- Connect to U10.OE#
+ hdmi_hpd_i : in std_logic; -- Connect to U10.HPD_SOURCE
+ hdmi_scl_io : inout std_logic; -- Connect to U10.SCL_SOURCE
+ hdmi_sda_io : inout std_logic; -- Connect to U10.SDA_SOURCE
+
+ -- MEGA65 smart keyboard controller
+ kb_io0_o : out std_logic; -- clock to keyboard
+ kb_io1_o : out std_logic; -- data output to keyboard
+ kb_io2_i : in std_logic; -- data input from keyboard
+ kb_tck_o : out std_logic;
+ kb_tdo_i : in std_logic;
+ kb_tms_o : out std_logic;
+ kb_tdi_o : out std_logic;
+ kb_jtagen_o : out std_logic;
+
+ -- SD Card
+ sd_reset_o : out std_logic;
+ sd_clk_o : out std_logic;
+ sd_mosi_o : out std_logic;
+ sd_miso_i : in std_logic;
+ sd_cd_i : in std_logic;
+ sd_d1_i : in std_logic;
+ sd_d2_i : in std_logic;
+
+ -- SD Connector (this is the slot at the bottom side of the case under the cover)
+ sd2_reset_o : out std_logic;
+ sd2_clk_o : out std_logic;
+ sd2_mosi_o : out std_logic;
+ sd2_miso_i : in std_logic;
+ sd2_cd_i : in std_logic;
+ sd2_wp_i : in std_logic;
+ sd2_d1_i : in std_logic;
+ sd2_d2_i : in std_logic;
+
+ -- Audio DAC. U37 = AK4432VT
+ -- I2C address 0x19
+ audio_mclk_o : out std_logic; -- Master Clock Input Pin, 12.288 MHz
+ audio_bick_o : out std_logic; -- Audio Serial Data Clock Pin, 3.072 MHz
+ audio_sdti_o : out std_logic; -- Audio Serial Data Input Pin, 16-bit LSB justified
+ audio_lrclk_o : out std_logic; -- Input Channel Clock Pin, 48.0 kHz
+ audio_pdn_n_o : out std_logic; -- Power-Down & Reset Pin
+ audio_i2cfil_o : out std_logic; -- I2C Interface Mode Select Pin
+ audio_scl_io : inout std_logic; -- Control Data Clock Input Pin
+ audio_sda_io : inout std_logic; -- Control Data Input/Output Pin
+
+ -- Joysticks and Paddles
+ fa_up_n_i : in std_logic;
+ fa_down_n_i : in std_logic;
+ fa_left_n_i : in std_logic;
+ fa_right_n_i : in std_logic;
+ fa_fire_n_i : in std_logic;
+ fa_fire_n_o : out std_logic; -- 0: Drive pin low (output). 1: Leave pin floating (input)
+ fa_up_n_o : out std_logic;
+ fa_left_n_o : out std_logic;
+ fa_down_n_o : out std_logic;
+ fa_right_n_o : out std_logic;
+ fb_up_n_i : in std_logic;
+ fb_down_n_i : in std_logic;
+ fb_left_n_i : in std_logic;
+ fb_right_n_i : in std_logic;
+ fb_fire_n_i : in std_logic;
+ fb_up_n_o : out std_logic;
+ fb_down_n_o : out std_logic;
+ fb_fire_n_o : out std_logic;
+ fb_right_n_o : out std_logic;
+ fb_left_n_o : out std_logic;
+
+ -- Joystick power supply
+ joystick_5v_disable_o : out std_logic; -- 1: Disable 5V power supply to joysticks
+ joystick_5v_powergood_i : in std_logic;
+
+ paddle_i : in std_logic_vector(3 downto 0);
+ paddle_drain_o : out std_logic;
+
+ -- HyperRAM. U29 = IS66WVH8M8DBLL-100B1LI
+ hr_d_io : inout unsigned(7 downto 0); -- Data/Address
+ hr_rwds_io : inout std_logic; -- RW Data strobe
+ hr_reset_o : out std_logic; -- Active low RESET line to HyperRAM
+ hr_clk_p_o : out std_logic;
+ hr_cs0_o : out std_logic;
+
+ -- I2C bus
+ -- U32 = PCA9655EMTTXG. Address 0x40. I/O expander.
+ -- U12 = MP8869SGL-Z. Address 0x61. DC/DC Converter.
+ -- U14 = MP8869SGL-Z. Address 0x67. DC/DC Converter.
+ i2c_scl_io : inout std_logic;
+ i2c_sda_io : inout std_logic;
+
+ -- CBM-488/IEC serial port
+ iec_reset_n_o : out std_logic;
+ iec_atn_n_o : out std_logic;
+ iec_clk_en_n_o : out std_logic;
+ iec_clk_n_i : in std_logic;
+ iec_clk_n_o : out std_logic;
+ iec_data_en_n_o : out std_logic;
+ iec_data_n_i : in std_logic;
+ iec_data_n_o : out std_logic;
+ iec_srq_en_n_o : out std_logic;
+ iec_srq_n_i : in std_logic;
+ iec_srq_n_o : out std_logic;
+
+ -- C64 Expansion Port (aka Cartridge Port)
+ cart_phi2_o : out std_logic;
+ cart_dotclock_o : out std_logic;
+ cart_dma_i : in std_logic;
+ cart_reset_oe_n_o : out std_logic;
+ cart_reset_io : inout std_logic;
+ cart_game_oe_n_o : out std_logic;
+ cart_game_io : inout std_logic;
+ cart_exrom_oe_n_o : out std_logic;
+ cart_exrom_io : inout std_logic;
+ cart_nmi_oe_n_o : out std_logic;
+ cart_nmi_io : inout std_logic;
+ cart_irq_oe_n_o : out std_logic;
+ cart_irq_io : inout std_logic;
+ cart_ctrl_en_o : out std_logic;
+ cart_ctrl_dir_o : out std_logic; -- =1 means FPGA->Port, =0 means Port->FPGA
+ cart_ba_io : inout std_logic;
+ cart_rw_io : inout std_logic;
+ cart_io1_io : inout std_logic;
+ cart_io2_io : inout std_logic;
+ cart_romh_oe_n_o : out std_logic;
+ cart_romh_io : inout std_logic;
+ cart_roml_oe_n_o : out std_logic;
+ cart_roml_io : inout std_logic;
+ cart_en_o : out std_logic;
+ cart_addr_en_o : out std_logic;
+ cart_haddr_dir_o : out std_logic; -- =1 means FPGA->Port, =0 means Port->FPGA
+ cart_laddr_dir_o : out std_logic; -- =1 means FPGA->Port, =0 means Port->FPGA
+ cart_a_io : inout unsigned(15 downto 0);
+ cart_data_en_o : out std_logic;
+ cart_data_dir_o : out std_logic; -- =1 means FPGA->Port, =0 means Port->FPGA
+ cart_d_io : inout unsigned(7 downto 0);
+
+ -- I2C bus for on-board peripherals
+ -- U36. 24AA025E48T. Address 0x50. 2K Serial EEPROM.
+ -- U38. RV-3032-C7. Address 0x51. Real-Time Clock Module.
+ -- U39. 24LC128. Address 0x56. 128K CMOS Serial EEPROM.
+ fpga_sda_io : inout std_logic;
+ fpga_scl_io : inout std_logic;
+
+ -- Connected to J18
+ grove_sda_io : inout std_logic;
+ grove_scl_io : inout std_logic;
+
+ -- On board LEDs
+ led_g_n_o : out std_logic;
+ led_r_n_o : out std_logic;
+ led_o : out std_logic;
+
+ -- Pmod Header
+ p1lo_io : inout std_logic_vector(3 downto 0);
+ p1hi_io : inout std_logic_vector(3 downto 0);
+ p2lo_io : inout std_logic_vector(3 downto 0);
+ p2hi_io : inout std_logic_vector(3 downto 0);
+ pmod1_en_o : out std_logic;
+ pmod1_flag_i : in std_logic;
+ pmod2_en_o : out std_logic;
+ pmod2_flag_i : in std_logic;
+
+ -- Quad SPI Flash. U5 = S25FL512SAGBHIS10
+ qspidb_io : inout std_logic_vector(3 downto 0);
+ qspicsn_o : out std_logic;
+
+ -- Debug.
+ dbg_11_io : inout std_logic;
+
+ -- SDRAM - 32M x 16 bit, 3.3V VCC. U44 = IS42S16320F-6BL
+ sdram_clk_o : out std_logic;
+ sdram_cke_o : out std_logic;
+ sdram_ras_n_o : out std_logic;
+ sdram_cas_n_o : out std_logic;
+ sdram_we_n_o : out std_logic;
+ sdram_cs_n_o : out std_logic;
+ sdram_ba_o : out std_logic_vector(1 downto 0);
+ sdram_a_o : out std_logic_vector(12 downto 0);
+ sdram_dqml_o : out std_logic;
+ sdram_dqmh_o : out std_logic;
+ sdram_dq_io : inout std_logic_vector(15 downto 0);
+
+ -- SMSC Ethernet PHY. U4 = KSZ8081RNDCA
+ eth_clock_o : out std_logic;
+ eth_led2_o : out std_logic;
+ eth_mdc_o : out std_logic;
+ eth_mdio_io : inout std_logic;
+ eth_reset_o : out std_logic;
+ eth_rxd_i : in std_logic_vector(1 downto 0);
+ eth_rxdv_i : in std_logic;
+ eth_rxer_i : in std_logic;
+ eth_txd_o : out std_logic_vector(1 downto 0);
+ eth_txen_o : out std_logic;
+
+ -- FDC interface
+ f_density_o : out std_logic;
+ f_diskchanged_i : in std_logic;
+ f_index_i : in std_logic;
+ f_motora_o : out std_logic;
+ f_motorb_o : out std_logic;
+ f_rdata_i : in std_logic;
+ f_selecta_o : out std_logic;
+ f_selectb_o : out std_logic;
+ f_side1_o : out std_logic;
+ f_stepdir_o : out std_logic;
+ f_step_o : out std_logic;
+ f_track0_i : in std_logic;
+ f_wdata_o : out std_logic;
+ f_wgate_o : out std_logic;
+ f_writeprotect_i : in std_logic
+
+ );
+end entity MEGA65;
+
+architecture beh of MEGA65 is
+
+ constant VIDEO_MODE : video_modes_t := C_HDMI_640x480p_60;
+
+ -- CPU control signals
+ signal cpu_addr : std_logic_vector(15 downto 0);
+ signal cpu_data_in : std_logic_vector(15 downto 0);
+ signal cpu_data_out : std_logic_vector(15 downto 0);
+ signal cpu_data_dir : std_logic;
+ signal cpu_data_valid : std_logic;
+ signal cpu_wait_for_data : std_logic;
+ signal cpu_halt : std_logic;
+ signal cpu_ins_cnt_strobe : std_logic;
+ signal cpu_int_n : std_logic;
+ signal cpu_igrant_n : std_logic;
+
+ -- MMIO control signals
+ signal rom_enable : std_logic;
+ signal rom_busy : std_logic;
+ signal rom_data_out : std_logic_vector(15 downto 0);
+ signal ram_enable : std_logic;
+ signal ram_busy : std_logic;
+ signal ram_data_out : std_logic_vector(15 downto 0);
+ signal pore_rom_enable : std_logic;
+ signal pore_rom_busy : std_logic;
+ signal pore_rom_data_out : std_logic_vector(15 downto 0);
+ signal til_reg0_enable : std_logic;
+ signal til_reg1_enable : std_logic;
+ signal switch_reg_enable : std_logic;
+ signal switch_data_out : std_logic_vector(15 downto 0);
+ signal kbd_en : std_logic;
+ signal kbd_we : std_logic;
+ signal kbd_reg : std_logic_vector(1 downto 0);
+ signal kbd_data_out : std_logic_vector(15 downto 0);
+ signal tin_en : std_logic;
+ signal tin_we : std_logic;
+ signal tin_reg : std_logic_vector(2 downto 0);
+ signal timer_data_out : std_logic_vector(15 downto 0);
+ signal vga_en : std_logic;
+ signal vga_we : std_logic;
+ signal vga_reg : std_logic_vector(3 downto 0);
+ signal vga_data_out : std_logic_vector(15 downto 0);
+ signal uart_en : std_logic;
+ signal uart_we : std_logic;
+ signal uart_reg : std_logic_vector(1 downto 0);
+ signal uart_data_out : std_logic_vector(15 downto 0);
+ signal uart_cpu_ws : std_logic;
+ signal cyc_en : std_logic;
+ signal cyc_we : std_logic;
+ signal cyc_reg : std_logic_vector(1 downto 0);
+ signal cyc_data_out : std_logic_vector(15 downto 0);
+ signal ins_en : std_logic;
+ signal ins_we : std_logic;
+ signal ins_reg : std_logic_vector(1 downto 0);
+ signal ins_data_out : std_logic_vector(15 downto 0);
+ signal eae_en : std_logic;
+ signal eae_we : std_logic;
+ signal eae_reg : std_logic_vector(2 downto 0);
+ signal eae_data_out : std_logic_vector(15 downto 0);
+ signal sd_en : std_logic;
+ signal sd_we : std_logic;
+ signal sd_reg : std_logic_vector(2 downto 0);
+ signal sd_data_out : std_logic_vector(15 downto 0);
+ signal hram_en : std_logic;
+ signal hram_we : std_logic;
+ signal hram_reg : std_logic_vector(3 downto 0);
+ signal hram_data_out : std_logic_vector(15 downto 0);
+ signal hram_cpu_ws : std_logic;
+
+ signal reset_pre_pore : std_logic;
+ signal reset_post_pore : std_logic;
+ signal reset_ctl : std_logic; -- combined pre- and post pore reset
+
+ -- VGA control signals
+ signal vga_r : std_logic;
+ signal vga_g : std_logic;
+ signal vga_b : std_logic;
+ signal vga_red : std_logic_vector(7 downto 0);
+ signal vga_green : std_logic_vector(7 downto 0);
+ signal vga_blue : std_logic_vector(7 downto 0);
+ signal vga_hsync : std_logic;
+ signal vga_vsync : std_logic;
+ signal vga_de : std_logic;
+
+ -- HDMI signals
+ signal tmds_clk : std_logic; -- HDMI pixel clock at 5x speed for TMDS @ 371.25 MHz
+ signal hdmi_clk : std_logic; -- HDMI pixel clock at normal speed @ 74.25 MHz
+ signal hdmi_rst : std_logic;
+ signal hdmi_tmds : slv_9_0_t(0 to 2); -- parallel TMDS symbol stream x 3 channels
+ signal hdmi_hs : std_logic;
+ signal hdmi_vs : std_logic;
+ signal hdmi_de : std_logic;
+ signal hdmi_red : std_logic_vector(7 downto 0);
+ signal hdmi_green : std_logic_vector(7 downto 0);
+ signal hdmi_blue : std_logic_vector(7 downto 0);
+
+ -- Clocks and related signals
+ signal clk_25Mhz : std_logic; -- video pixel clock
+ signal clk_50MHz : std_logic; -- 50 MHz clock. aiming for 100 MHz
+ signal clk_100MHz : std_logic; -- 100 MHz clock created by mmcme2 for congruent phase
+ signal clk_200MHz : std_logic; -- 200 MHz
+ signal clk_125MHz : std_logic; -- 125 MHz TMDS clock
+ signal clk_fb_main : std_logic;
+ signal pll_locked_main : std_logic;
+
+ -- enable displaying of address bus on system halt, if switch 2 is on
+ signal i_til_reg0_enable : std_logic;
+ signal i_til_data_in : std_logic_vector(15 downto 0);
+
+ -- emulate the switches on the Nexys4 dev board to toggle VGA and PS/2
+ signal SWITCHES : std_logic_vector(15 downto 0);
+
+begin
+
+ -- Merge data outputs from all devices into a single data input to the CPU.
+ -- This requires that all devices output 0's when not selected.
+ cpu_data_in <= pore_rom_data_out or
+ rom_data_out or
+ ram_data_out or
+ switch_data_out or
+ kbd_data_out or
+ vga_data_out or
+ uart_data_out or
+ timer_data_out or
+ cyc_data_out or
+ ins_data_out or
+ eae_data_out or
+ sd_data_out or
+ hram_data_out;
+
+ -- Due to a bug in the R5/R6 boards, the cartridge port needs to be enabled for joystick port 2 to work
+ cart_en_o <= '1';
+
+ -- Safe default values for not-used/unsupported ports
+
+ vdac_psave_n_o <= '1';
+ hdmi_hiz_en_o <= '0'; -- HDMI is 50 ohm terminated.
+ hdmi_ls_oe_n_o <= '0'; -- Enable HDMI output
+ hdmi_scl_io <= 'Z';
+ hdmi_sda_io <= 'Z';
+
+ dbg_11_io <= 'Z';
+
+ eth_clock_o <= '0';
+ eth_led2_o <= '0';
+ eth_mdc_o <= '0';
+ eth_mdio_io <= 'Z';
+ eth_reset_o <= '1';
+ eth_txd_o <= (others => '0');
+ eth_txen_o <= '0';
+
+ f_density_o <= '1';
+ f_motora_o <= '1';
+ f_motorb_o <= '1';
+ f_selecta_o <= '1';
+ f_selectb_o <= '1';
+ f_side1_o <= '1';
+ f_stepdir_o <= '1';
+ f_step_o <= '1';
+ f_wdata_o <= '1';
+ f_wgate_o <= '1';
+
+ joystick_5v_disable_o <= '0'; -- Enable 5V power supply to joysticks
+
+ led_g_n_o <= '1'; -- Off
+ led_r_n_o <= '1'; -- Off
+ led_o <= '0'; -- Off
+
+ p1lo_io <= (others => 'Z');
+ p1hi_io <= (others => 'Z');
+ p2lo_io <= (others => 'Z');
+ p2hi_io <= (others => 'Z');
+
+ pmod1_en_o <= '0';
+ pmod2_en_o <= '0';
+
+ qspidb_io <= (others => 'Z');
+ qspicsn_o <= '1';
+
+ sdram_clk_o <= '0';
+ sdram_cke_o <= '0';
+ sdram_ras_n_o <= '1';
+ sdram_cas_n_o <= '1';
+ sdram_we_n_o <= '1';
+ sdram_cs_n_o <= '1';
+ sdram_ba_o <= (others => '0');
+ sdram_a_o <= (others => '0');
+ sdram_dqml_o <= '0';
+ sdram_dqmh_o <= '0';
+ sdram_dq_io <= (others => 'Z');
+
+ clk_main : mmcme2_base
+ generic map
+ (
+ clkin1_period => 10.0, -- 100 MHz (10 ns)
+ clkfbout_mult_f => 10.0, -- 1000 MHz common multiply
+ divclk_divide => 1, -- 1000 MHz /1 common divide to stay within 600MHz-1600MHz range
+ clkout0_divide_f => 40.0, -- 25 MHz
+ clkout1_divide => 10, -- 100 MHz
+ clkout2_divide => 20, -- 50 MHz
+ clkout3_divide => 5, -- 200 MHz
+ clkout4_divide => 8 -- 125 MHz
+ )
+ port map
+ (
+ pwrdwn => '0',
+ rst => '0',
+ clkin1 => clk_i,
+ clkfbin => clk_fb_main,
+ clkfbout => clk_fb_main,
+ clkout0 => clk_25MHz, -- 25 MHz video pixel clock
+ clkout1 => clk_100MHz, -- 100 MHz
+ clkout2 => clk_50MHz, -- 50 MHz
+ clkout3 => clk_200MHz, -- 200 MHz
+ clkout4 => clk_125MHz, -- HDMI TMDS clock
+ locked => pll_locked_main
+ );
+
+ -- QNICE CPU
+ cpu : entity work.QNICE_CPU
+ port map
+ (
+ CLK => clk_50MHz,
+ RESET => reset_ctl,
+ WAIT_FOR_DATA => cpu_wait_for_data,
+ ADDR => cpu_addr,
+ DATA_IN => cpu_data_in,
+ DATA_OUT => cpu_data_out,
+ DATA_DIR => cpu_data_dir,
+ DATA_VALID => cpu_data_valid,
+ HALT => cpu_halt,
+ INS_CNT_STROBE => cpu_ins_cnt_strobe,
+ INT_N => cpu_int_n,
+ IGRANT_N => cpu_igrant_n
+ );
+
+ -- ROM: up to 64kB consisting of up to 32.000 16 bit words
+ rom : entity work.BROM
+ generic map(
+ FILE_NAME => ROM_FILE
+ )
+ port map
+ (
+ clk => clk_50MHz,
+ ce => rom_enable,
+ address => cpu_addr(14 downto 0),
+ data => rom_data_out,
+ busy => rom_busy
+ );
+
+ -- RAM: up to 64kB consisting of up to 32.000 16 bit words
+ ram : entity work.BRAM
+ port map
+ (
+ clk => clk_50MHz,
+ ce => ram_enable,
+ address => cpu_addr(14 downto 0),
+ we => cpu_data_dir,
+ data_i => cpu_data_out,
+ data_o => ram_data_out,
+ busy => ram_busy
+ );
+
+ -- PORE ROM: Power On & Reset Execution ROM
+ -- contains code that is executed during power on and/or during reset
+ -- MMIO is managing the PORE process
+ pore_rom : entity work.BROM
+ generic map(
+ FILE_NAME => PORE_ROM_FILE
+ )
+ port map
+ (
+ clk => clk_50MHz,
+ ce => pore_rom_enable,
+ address => cpu_addr(14 downto 0),
+ data => pore_rom_data_out,
+ busy => pore_rom_busy
+ );
+
+ -- VGA: 80x40 textmode VGA adaptor
+ vga_screen : entity work.vga_textmode
+ port map
+ (
+ reset => reset_ctl,
+ clk25MHz => clk_25MHz,
+ clk50MHz => clk_50MHz,
+ R => vga_r,
+ G => vga_g,
+ B => vga_b,
+ hsync => vga_hsync,
+ vsync => vga_vsync,
+ hdmi_de => vga_de, -- naming inconsistency: vga_de is hdmi_de in vga's clock domain
+ en => vga_en,
+ we => vga_we,
+ reg => vga_reg,
+ data_in => cpu_data_out,
+ data_out => vga_data_out
+ );
+
+ -- VGA: wire the simplified color system of the VGA component to the VGA outputs
+ vga_red <= (others => vga_r);
+ vga_green <= (others => vga_g);
+ vga_blue <= (others => vga_b);
+
+ -- special UART with FIFO that can be directly connected to the CPU bus
+ uart : entity work.bus_uart
+ generic map(
+ DIVISOR => UART_DIVISOR
+ )
+ port map
+ (
+ clk => clk_50MHz,
+ reset => reset_ctl,
+ rx => uart_rxd_i,
+ tx => uart_txd_o,
+ rts => '0',
+ cts => open,
+ uart_en => uart_en,
+ uart_we => uart_we,
+ uart_reg => uart_reg,
+ uart_cpu_ws => uart_cpu_ws,
+ cpu_data_in => cpu_data_out,
+ cpu_data_out => uart_data_out
+ );
+
+ -- MEGA65 keyboard
+ kbd : entity work.keyboard
+ generic map(
+ clk_freq => 50000000
+ )
+ port map
+ (
+ clk => clk_50MHz,
+ reset => reset_ctl,
+ kb_io0 => kb_io0_o,
+ kb_io1 => kb_io1_o,
+ kb_io2 => kb_io2_i,
+ kbd_en => kbd_en,
+ kbd_we => kbd_we,
+ kbd_reg => kbd_reg,
+ cpu_data_in => cpu_data_out,
+ cpu_data_out => kbd_data_out,
+ stdinout => SWITCHES(1 downto 0)
+ );
+
+ timer_interrupt : entity work.timer_module
+ generic map(
+ CLK_FREQ => 50000000
+ )
+ port map
+ (
+ clk => clk_50MHz,
+ reset => reset_ctl,
+ int_n_out => cpu_int_n,
+ grant_n_in => cpu_igrant_n,
+ int_n_in => '1', -- no more devices to in Daisy Chain: 1=no interrupt
+ grant_n_out => open, -- ditto: open=grant goes nowhere
+ en => tin_en,
+ we => tin_we,
+ reg => tin_reg,
+ data_in => cpu_data_out,
+ data_out => timer_data_out
+ );
+
+ -- cycle counter
+ cyc : entity work.cycle_counter
+ port map
+ (
+ clk => clk_50MHz,
+ impulse => '1',
+ reset => reset_ctl,
+ en => cyc_en,
+ we => cyc_we,
+ reg => cyc_reg,
+ data_in => cpu_data_out,
+ data_out => cyc_data_out
+ );
+
+ -- instruction counter
+ ins : entity work.cycle_counter
+ port map
+ (
+ clk => clk_50MHz,
+ impulse => cpu_ins_cnt_strobe,
+ reset => reset_ctl,
+ en => ins_en,
+ we => ins_we,
+ reg => ins_reg,
+ data_in => cpu_data_out,
+ data_out => ins_data_out
+ );
+
+ -- EAE - Extended Arithmetic Element (32-bit multiplication, division, modulo)
+ eae_inst : entity work.eae
+ port map
+ (
+ clk => clk_50MHz,
+ reset => reset_ctl,
+ en => eae_en,
+ we => eae_we,
+ reg => eae_reg,
+ data_in => cpu_data_out,
+ data_out => eae_data_out
+ );
+
+ -- SD Card
+ sd_card : entity work.sdcard
+ port map
+ (
+ clk => clk_50MHz,
+ reset => reset_ctl,
+ en => sd_en,
+ we => sd_we,
+ reg => sd_reg,
+ data_in => cpu_data_out,
+ data_out => sd_data_out,
+ sd_reset => sd_reset_o,
+ sd_clk => sd_clk_o,
+ sd_mosi => sd_mosi_o,
+ sd_miso => sd_miso_i
+ );
+
+ -- HyperRAM
+ HRAM : entity work.hyperram_ctl
+ port map
+ (
+ clk => clk_50MHz,
+ clk2x => clk_100MHz,
+ clk4x => clk_200MHz,
+ reset => reset_ctl,
+ hram_en => hram_en,
+ hram_we => hram_we,
+ hram_reg => hram_reg,
+ hram_cpu_ws => hram_cpu_ws,
+ data_in => cpu_data_out,
+ data_out => hram_data_out,
+ hr_d => hr_d_io,
+ hr_rwds => hr_rwds_io,
+ hr_reset => hr_reset_o,
+ hr_clk_p => hr_clk_p_o,
+ hr_cs0 => hr_cs0_o
+ );
+
+ -- memory mapped i/o controller
+ mmio_controller : entity work.mmio_mux
+ generic map(
+ GD_TIL => false, -- no support for TIL leds on MEGA65
+ GD_SWITCHES => true, -- we emulate the switch register as described in doc/README.md
+ GD_HRAM => true -- support HyperRAM
+ )
+ port map
+ (
+ HW_RESET => reset_button_i,
+ CLK => clk_50MHz, -- @TODO change debouncer bitsize when going to 100 MHz
+ addr => cpu_addr,
+ data_dir => cpu_data_dir,
+ data_valid => cpu_data_valid,
+ cpu_wait_for_data => cpu_wait_for_data,
+ cpu_halt => cpu_halt,
+ cpu_igrant_n => cpu_igrant_n,
+ rom_enable => rom_enable,
+ rom_busy => rom_busy,
+ ram_enable => ram_enable,
+ ram_busy => ram_busy,
+ pore_rom_enable => pore_rom_enable,
+ pore_rom_busy => pore_rom_busy,
+ switch_reg_enable => switch_reg_enable,
+ kbd_en => kbd_en,
+ kbd_we => kbd_we,
+ kbd_reg => kbd_reg,
+ tin_en => tin_en,
+ tin_we => tin_we,
+ tin_reg => tin_reg,
+ vga_en => vga_en,
+ vga_we => vga_we,
+ vga_reg => vga_reg,
+ uart_en => uart_en,
+ uart_we => uart_we,
+ uart_reg => uart_reg,
+ uart_cpu_ws => uart_cpu_ws,
+ cyc_en => cyc_en,
+ cyc_we => cyc_we,
+ cyc_reg => cyc_reg,
+ ins_en => ins_en,
+ ins_we => ins_we,
+ ins_reg => ins_reg,
+ eae_en => eae_en,
+ eae_we => eae_we,
+ eae_reg => eae_reg,
+ sd_en => sd_en,
+ sd_we => sd_we,
+ sd_reg => sd_reg,
+ hram_en => hram_en,
+ hram_we => hram_we,
+ hram_reg => hram_reg,
+ hram_cpu_ws => hram_cpu_ws,
+
+ -- no TIL leds on the MEGA65 ,
+ til_reg0_enable => open,
+ til_reg1_enable => open,
+
+ reset_pre_pore => reset_pre_pore,
+ reset_post_pore => reset_post_pore
+ );
+
+ -- emulate the toggle switches as described in doc/README.md
+ switch_driver : process (switch_reg_enable, SWITCHES)
+ begin
+ if switch_reg_enable = '1' then
+ switch_data_out <= SWITCHES;
+ else
+ switch_data_out <= (others => '0');
+ end if;
+ end process;
+
+ video_signal_latches : process (clk_25MHz)
+ begin
+ -- latching values at different edge than sampling by dac eliminates blurring
+ if falling_edge(clk_25MHz) then
+ vga_red_o <= vga_red;
+ vga_green_o <= vga_green;
+ vga_blue_o <= vga_blue;
+ vga_hs_o <= vga_hsync;
+ vga_vs_o <= vga_vsync;
+ end if;
+ end process;
+
+ -- make the VDAC output the image
+ vdac_sync_n_o <= '0';
+ vdac_blank_n_o <= '1';
+ vdac_clk_o <= clk_25MHz;
+
+ -- emulate the switches on the Nexys4 to toggle VGA and PS/2 keyboard
+ -- bit #0: use UART as STDIN (0) / use MEGA65 keyboard as STDIN (1)
+ -- bit #1: use UART AS STDOUT (0) / use VGA as STDOUT (1)
+ SWITCHES(15 downto 2) <= "00000000000000";
+
+ -- generate the general reset signal
+ reset_ctl <= '1' when (reset_pre_pore = '1' or reset_post_pore = '1' or pll_locked_main = '0') else
+ '0';
+
+ -- HDMI pixel clock and VGA pixel clock are the same, TMDS clock is directly derived
+ -- therefore CDC part can be ommitted (?)
+
+ hdmi_clk <= clk_25MHz;
+ tmds_clk <= clk_125MHz;
+
+ hdmi_rst <= reset_ctl;
+ hdmi_hs <= vga_hsync;
+ hdmi_vs <= vga_vsync;
+ hdmi_de <= vga_de;
+ hdmi_red <= vga_red;
+ hdmi_green <= vga_green;
+ hdmi_blue <= vga_blue;
+
+ hdmi_data_gen : for i in 0 to 2 generate
+ begin
+ hdmi_data_inst : entity work.serialiser_10to1_selectio
+ port map
+ (
+ rst => hdmi_rst,
+ clk => hdmi_clk,
+ clk_x5 => tmds_clk,
+ d => hdmi_tmds(i),
+ out_p => tmds_data_p_o(i),
+ out_n => tmds_data_n_o(i)
+ );
+ end generate hdmi_data_gen;
+
+ hdmi_clk_gen : entity work.serialiser_10to1_selectio
+ port map
+ (
+ rst => hdmi_rst,
+ clk => hdmi_clk,
+ clk_x5 => tmds_clk,
+ d => "0000011111",
+ out_p => tmds_clk_p_o,
+ out_n => tmds_clk_n_o
+ ); -- hdmi_clk_inst
+
+ vga_to_hdmi_inst : entity work.vga_to_hdmi
+ port map
+ (
+ select_44100 => '0',
+ dvi => '0',
+ vic => std_logic_vector(to_unsigned(VIDEO_MODE.CEA_CTA_VIC, 8)),
+ aspect => VIDEO_MODE.ASPECT,
+ pix_rep => VIDEO_MODE.PIXEL_REP,
+ vs_pol => VIDEO_MODE.V_POL,
+ hs_pol => VIDEO_MODE.H_POL,
+
+ vga_rst => hdmi_rst,
+ vga_clk => hdmi_clk,
+ vga_vs => hdmi_vs,
+ vga_hs => hdmi_hs,
+ vga_de => hdmi_de,
+ vga_r => hdmi_red,
+ vga_g => hdmi_green,
+ vga_b => hdmi_blue,
+
+ -- PCM audio
+ pcm_clk => '0',
+ pcm_rst => '0',
+ pcm_clken => '0',
+ pcm_l => (others => '0'),
+ pcm_r => (others => '0'),
+ pcm_acr => '0',
+ pcm_n => (others => '0'),
+ pcm_cts => (others => '0'),
+
+ -- TMDS output (parallel)
+ tmds => hdmi_tmds
+ ); -- vga_to_hdmi_inst
+
+end architecture beh;
diff --git a/vhdl/hw/MEGA65/drivers/hyperram_r6.vhdl b/vhdl/hw/MEGA65/drivers/hyperram_r6.vhdl
new file mode 100644
index 00000000..8c3912b5
--- /dev/null
+++ b/vhdl/hw/MEGA65/drivers/hyperram_r6.vhdl
@@ -0,0 +1,3294 @@
+-- Original MEGA65 keyboard driver file by Paul Gardner-Stephen
+-- see README.md for details and license
+--
+-- Modified for QNICE-FPGA by sy2002 in August 2020
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use ieee.numeric_std.all;
+
+package cache_row_type is
+type cache_row_t is array (0 to 7) of unsigned(7 downto 0);
+end package cache_row_type;
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use ieee.numeric_std.all;
+use Std.TextIO.all;
+
+use work.cache_row_type.all;
+
+entity hyperram is
+ generic ( in_simulation : in boolean := false);
+ Port ( pixelclock : in STD_LOGIC; -- For slow devices bus interface is
+ -- actually on pixelclock to reduce latencies
+ -- Also pixelclock is the natural clock speed we apply to the HyperRAM.
+ clock163 : in std_logic; -- Used for fast clock for HyperRAM
+ clock325 : in std_logic; -- Used for fast clock for HyperRAM SERDES units
+
+ -- Simple counter for number of requests received
+ request_counter : out std_logic := '0';
+
+ read_request : in std_logic;
+ write_request : in std_logic;
+ address : in unsigned(26 downto 0);
+ wdata : in unsigned(7 downto 0);
+
+ -- Optional 16-bit interface (for Amiga core use)
+ -- (That it is optional, is why the write_en is inverted for the
+ -- low-byte).
+ -- 16-bit transactions MUST occur on an even numbered address, or
+ -- else expect odd and horrible things to happen.
+ wdata_hi : in unsigned(7 downto 0) := x"00";
+ wen_hi : in std_logic := '0';
+ wen_lo : in std_logic := '1';
+ rdata_hi : out unsigned(7 downto 0);
+ rdata_16en : in std_logic := '0'; -- set this high to be able
+ -- to read 16-bit values
+
+ rdata : out unsigned(7 downto 0);
+
+ data_ready_strobe : out std_logic := '0';
+ busy : out std_logic := '0';
+
+ -- Export current cache line for speeding up reads from slow_devices controller
+ -- by skipping the need to hand us the request and get the response back.
+ current_cache_line : out cache_row_t := (others => (others => '0'));
+ current_cache_line_address : inout unsigned(26 downto 3) := (others => '0');
+ current_cache_line_valid : out std_logic := '0';
+ expansionram_current_cache_line_next_toggle : in std_logic := '0';
+
+ -- Allow VIC-IV to request lines of data also.
+ -- We then pump it out byte-by-byte when ready
+ -- VIC-IV can address only 512KB at a time, so we have a banking register
+ viciv_addr : in unsigned(18 downto 3) := (others => '0');
+ viciv_request_toggle : in std_logic := '0';
+ viciv_data_out : out unsigned(7 downto 0) := x"00";
+ viciv_data_strobe : out std_logic := '0';
+
+ hr_d : inout unsigned(7 downto 0) := (others => 'Z'); -- Data/Address
+ hr_rwds : inout std_logic := 'Z'; -- RW Data strobe
+ hr_reset : out std_logic := '1'; -- Active low RESET line to HyperRAM
+ hr_clk_n : out std_logic := '0';
+ hr_clk_p : out std_logic := '1';
+ hr_cs0 : out std_logic := '1'
+ );
+end hyperram;
+
+architecture gothic of hyperram is
+
+ type state_t is (
+ StartupDelay,
+ ReadAbort,
+ Idle,
+ ReadSetup,
+ WriteSetup,
+ HyperRAMOutputCommand,
+ HyperRAMDoWrite,
+ HyperRAMOutputCommandSlow,
+ StartBackgroundWrite,
+ HyperRAMDoWriteSlow,
+ HyperRAMFinishWriting,
+ HyperRAMReadWaitSlow,
+ HyperRAMReadWait
+ );
+
+ -- How many clock ticks need to expire between transactions to satisfy T_RWR
+ -- of hyperrram for the T_RWR 40ns delay.
+ -- We can also subtract one cycle for the time it takes to pull CS low, and then
+ -- two more for the clocks before the critical moment, and one more for time
+ -- covered by various latencies in the system (including clock 1/2 cycle delay).
+ -- This effectively gets us down to 45ns. Taking another cycle would leave us
+ -- at only 38.7ns, which is a bit too short.
+ -- This gives us an effective 8-byte write latency of ~132ns = ~7.5MHz.
+ -- For read it is ~143ns = 6.99MHz, which might just be a whisker too slow
+ -- for MiniMig. By reading only 4 bytes instead of 8, this would allow getting
+ -- back down to ~120 -- 132ns, which should be enough.
+ -- Actually, all of that is a bit moot, since it seems that we just have to apply
+ -- some trial and error to get it right. 1 seems right with the current settings.
+ signal rwr_delay : unsigned(7 downto 0) := to_unsigned(1,8);
+ signal rwr_counter : unsigned(7 downto 0) := (others => '0');
+ signal rwr_waiting : std_logic := '0';
+
+ signal current_cache_line_drive : cache_row_t := (others => (others => '0'));
+ signal current_cache_line_address_drive : unsigned(26 downto 3) := (others => '0');
+ signal current_cache_line_valid_drive : std_logic := '0';
+
+ signal last_current_cache_next_toggle : std_logic := '0';
+
+ signal state : state_t := StartupDelay;
+ signal busy_internal : std_logic := '1';
+ signal hr_command : unsigned(47 downto 0);
+
+ signal hr_d_last : unsigned(7 downto 0);
+
+ -- Used to assert CS line on BOTH hyperRAM modules at the same time
+ -- when doing the initial configuration register write.
+ signal first_transaction : std_logic := '1';
+
+ -- Initial transaction is config register write
+ signal config_reg_write : std_logic := '1';
+ signal ram_address : unsigned(26 downto 0) :=
+ "010000000000001000000000000"; -- = bottom 27 bits of x"A001000";
+ signal ram_wdata : unsigned(7 downto 0) := x"00";
+ signal ram_wdata_hi : unsigned(7 downto 0) := x"00";
+ signal ram_wdata_enlo : std_logic := '0';
+ signal ram_wdata_enhi : std_logic := '0';
+ signal ram_reading : std_logic := '0';
+ signal ram_reading_held : std_logic := '0';
+
+ signal ram_reading_drive : std_logic := '0';
+ signal ram_address_drive : unsigned(26 downto 0) :=
+ "010000000000001000000000000"; -- = bottom 27 bits of x"A001000";
+ signal ram_wdata_drive : unsigned(7 downto 0) := x"00";
+ signal ram_wdata_hi_drive : unsigned(7 downto 0) := x"00";
+ signal ram_wdata_enlo_drive : std_logic := '0';
+ signal ram_wdata_enhi_drive : std_logic := '0';
+ signal cache_row0_address_matches_ram_address : std_logic := '0';
+ signal cache_row1_address_matches_ram_address : std_logic := '0';
+ signal ram_address_matches_current_cache_line_address : std_logic := '0';
+ signal address_matches_hyperram_access_address_block : std_logic := '0';
+ signal write_collect0_address_matches_write_collect1_address_plus_1 : std_logic := '0';
+
+ -- We want to set config register 0 to $ffe6, to enable variable latency
+ -- and 3 cycles instead of 6 for latency. This speeds up writing almost 2x.
+ -- But at 80MHz instead of 40MHz bus, we have to increase the latency from
+ -- 3 to 4 cycles to satisfy the 40ns minimum time requirement.
+ -- This also sets the drive strength to the maximum, to get cleaner faster
+ -- clock transitions. This fixes checkerboard read errors at 80MHz.
+
+ signal conf_buf0 : unsigned(7 downto 0) := x"ff";
+ signal conf_buf1 : unsigned(7 downto 0) := x"f6";
+ signal conf_buf0_in : unsigned(7 downto 0) := x"ff";
+ signal conf_buf1_in : unsigned(7 downto 0) := x"f6";
+ signal conf_buf0_set : std_logic := '0';
+ signal conf_buf1_set : std_logic := '0';
+ signal last_conf_buf0_set : std_logic := '0';
+ signal last_conf_buf1_set : std_logic := '0';
+
+ -- 4 is correct for the part we have in the MEGA65, after we have set the
+ -- config register to minimise latency.
+ signal write_latency : unsigned(7 downto 0) := to_unsigned(5,8);
+ -- And the matching extra latency is 5
+ signal extra_write_latency : unsigned(7 downto 0) := to_unsigned(7,8);
+
+ -- And for the 2nd trap-door hyperram.
+ -- That module from 1BitSquared uses a different brand of hyperram
+ -- and seems to have different timing.
+ signal write_latency2 : unsigned(7 downto 0) := to_unsigned(3,8);
+ signal extra_write_latency2 : unsigned(7 downto 0) := to_unsigned(1,8);
+
+
+ -- Control optimisations for hyperram access
+ -- Enabling the cache MOSTLY works, but there is some cache coherency bug(s)
+ -- when writing. These are currently being investigated.
+ signal cache_enabled : boolean := true;
+ signal block_read_enable : std_logic := '1'; -- enable 32 byte read block fetching
+ signal flag_prefetch : std_logic := '1'; -- enable/disable prefetch of read
+ -- blocks
+ signal enable_current_cache_line : std_logic := '1';
+
+ -- These three must be off for reliable operation on current PCBs
+ signal fast_cmd_mode : std_logic := '0';
+ signal fast_read_mode : std_logic := '0';
+ signal fast_write_mode : std_logic := '0';
+
+ signal read_phase_shift : std_logic := '0';
+ signal write_phase_shift : std_logic := '1';
+
+ signal countdown : integer range 0 to 63 := 0;
+ signal countdown_is_zero : std_logic := '1';
+ signal extra_latency : std_logic := '0';
+ signal countdown_timeout : std_logic := '0';
+
+ signal pause_phase : std_logic := '0';
+ signal hr_clock : std_logic := '0';
+
+ signal data_ready_toggle : std_logic := '0';
+ signal last_data_ready_toggle : std_logic := '0';
+ signal data_ready_strobe_hold : std_logic := '0';
+
+ signal request_toggle : std_logic := '0';
+ signal request_accepted : std_logic := '0';
+ signal last_request_toggle : std_logic := '0';
+
+ signal byte_phase : unsigned(5 downto 0) := to_unsigned(0,6);
+ signal write_byte_phase : std_logic := '0';
+
+ signal hr_ddr : std_logic := '0';
+ signal hr_rwds_ddr : std_logic := '0';
+ signal hr_reset_int : std_logic := '1';
+ signal hr_rwds_int : std_logic := '0';
+ signal hr_cs0_int : std_logic := '0';
+ signal hr_clk_p_int : std_logic := '0';
+ signal hr_clk_n_int : std_logic := '0';
+
+ signal cycle_count : integer := 0;
+
+ -- Have a tiny little cache to reduce latency
+ -- 8 byte cache rows, where we indicate the validity of
+ -- each byte.
+ signal cache_row0_valids : std_logic_vector(0 to 7) := (others => '0');
+ signal cache_row0_address : unsigned(23 downto 0) := (others => '1');
+ signal cache_row0_data : cache_row_t := ( others => x"00" );
+
+ signal cache_row1_valids : std_logic_vector(0 to 7) := (others => '0');
+ signal cache_row1_address : unsigned(23 downto 0) := (others => '1');
+ signal cache_row1_data : cache_row_t := ( others => x"00" );
+
+ -- Collect writes together to hide write latency
+ signal write_collect0_dispatchable : std_logic := '0';
+ signal write_collect0_address : unsigned(26 downto 3) := (others => '0');
+ signal write_collect0_valids : std_logic_vector(0 to 7) := (others => '0');
+ signal write_collect0_data : cache_row_t := ( others => x"00" );
+ signal write_collect0_toolate : std_logic := '0'; -- Set when its too late to
+ -- add more bytes to the write.
+ signal write_collect0_flushed : std_logic := '1';
+
+ signal write_collect1_dispatchable : std_logic := '0';
+ signal write_collect1_address : unsigned(26 downto 3) := (others => '0');
+ signal write_collect1_valids : std_logic_vector(0 to 7) := (others => '0');
+ signal write_collect1_data : cache_row_t := ( others => x"00" );
+ signal write_collect1_toolate : std_logic := '0'; -- Set when its too late to
+ -- add more bytes to the write.
+ signal write_collect1_flushed : std_logic := '1';
+
+
+ type block_t is array (0 to 3) of cache_row_t;
+ signal block_data : block_t := (others => (others => x"00"));
+ signal block_address : unsigned(26 downto 5);
+ signal block_valid : std_logic := '0';
+ signal is_block_read : boolean := false;
+ signal is_prefetch : boolean := false;
+ signal is_expected_to_respond : boolean := false;
+ signal ram_prefetch : boolean := false;
+ signal ram_normalfetch : boolean := false;
+
+ signal current_cache_line_new_address : unsigned(26 downto 3) := (others => '0');
+ signal current_cache_line_update : cache_row_t := (others => (others => '0'));
+ signal current_cache_line_update_address : unsigned(26 downto 3) := (others => '0');
+ signal current_cache_line_update_all : std_logic := '0';
+ signal current_cache_line_update_flags : std_logic_vector(0 to 7) := (others => '0');
+ signal last_current_cache_line_update_all : std_logic := '0';
+ signal last_current_cache_line_update_flags : std_logic_vector(0 to 7) := (others => '0');
+
+ signal current_cache_line_matches_block : std_logic := '0';
+ signal current_cache_line_plus_1_matches_block : std_logic := '0';
+ signal hyperram_access_address_matches_cache_row0 : std_logic := '0';
+ signal hyperram_access_address_matches_cache_row1 : std_logic := '0';
+
+ signal cache_row_update_toggle : std_logic := '0';
+ signal last_cache_row_update_toggle : std_logic := '0';
+ signal cache_row_update_address : unsigned(26 downto 3) := (others => '0');
+ signal cache_row_update_byte : integer range 0 to 7 := 0;
+ signal cache_row_update_value : unsigned(7 downto 0) := x"00";
+ signal cache_row_update_value_hi : unsigned(7 downto 0) := x"00";
+ signal cache_row_update_lo : std_logic := '0';
+ signal cache_row_update_hi : std_logic := '0';
+
+ signal last_rwds : std_logic := '0';
+
+ signal fake_data_ready_strobe : std_logic := '0';
+ signal fake_rdata : unsigned(7 downto 0) := x"00";
+ signal fake_rdata_hi : unsigned(7 downto 0) := x"00";
+
+ signal request_counter_int : std_logic := '0';
+
+
+ signal hr_rwds_high_seen : std_logic := '0';
+
+ signal random_bits : unsigned(7 downto 0) := x"00";
+
+ signal write_blocked : std_logic := '0';
+
+ signal background_write : std_logic := '0';
+ signal background_write_source : std_logic := '0';
+ signal background_write_valids : std_logic_vector(0 to 7) := x"00";
+ signal background_write_data : cache_row_t := (others => (others => '0'));
+ signal background_write_count : integer range 0 to 7 := 0;
+ signal background_write_next_address : unsigned(26 downto 3) := (others => '0');
+ signal background_write_next_address_matches_collect0 : std_logic := '0';
+ signal background_write_next_address_matches_collect1 : std_logic := '0';
+ signal background_chained_write : std_logic := '0';
+ signal background_write_fetch : std_logic := '0';
+ signal collect1_matches_collect0_plus_1 : std_logic := '0';
+ signal collect0_matches_collect1_plus_1 : std_logic := '0';
+ signal matches_cache_row_update_address : std_logic := '0';
+ signal cache_row_update_address_changed : std_logic := '0';
+ signal block_address_matches_cache_row_update_address : std_logic := '0';
+ signal cache_row0_address_matches_cache_row_update_address : std_logic := '0';
+ signal cache_row1_address_matches_cache_row_update_address : std_logic := '0';
+ signal byte_phase_greater_than_address_low_bits : std_logic := '0';
+ signal byte_phase_greater_than_address_end_of_row : std_logic := '0';
+ signal block_address_matches_address : std_logic := '0';
+ signal invalidate_read_cache : std_logic := '0';
+
+ signal write_continues : integer range 0 to 255 := 0;
+ signal write_continues_max : integer range 0 to 255 := 16;
+
+ -- If we get too many writes in short succession, we may need to queue up to
+ -- two of the writes, while waiting for slow_devices to notice
+ signal queued_write : std_logic := '0';
+ signal queued_wen_lo : std_logic := '0';
+ signal queued_wen_hi : std_logic := '0';
+ signal queued_wdata : unsigned(7 downto 0) := x"00";
+ signal queued_wdata_hi : unsigned(7 downto 0) := x"00";
+ signal queued_waddr : unsigned(26 downto 0) := to_unsigned(0,27);
+ signal queued2_write : std_logic := '0';
+ signal queued2_wen_lo : std_logic := '0';
+ signal queued2_wen_hi : std_logic := '0';
+ signal queued2_wdata : unsigned(7 downto 0) := x"00";
+ signal queued2_wdata_hi : unsigned(7 downto 0) := x"00";
+ signal queued2_waddr : unsigned(26 downto 0) := to_unsigned(0,27);
+
+ -- Delay sending of the initial configuration write command
+ -- to give the HyperRAM chip time to start up
+ -- Datasheet says 150usec is required, we do that, plus a bit.
+ signal start_delay_counter : integer
+ := 150*(1000/162)+20
+ -- plus a correction factor to get initial config register write correctly
+ -- aligned with the clock
+ +2;
+ signal start_delay_expired : std_logic := '0';
+
+ -- phaseshift has to also start at 1 for the above to work.
+ signal hr_clk_phaseshift : std_logic := '1';
+ signal hr_clk_phaseshift_current : std_logic := '1';
+ signal last_hr_clk_phaseshift : std_logic := '1';
+
+ signal hr_clk_fast : std_logic := '1';
+ signal hr_clk_fast_current : std_logic := '1';
+ signal hr_clk : std_logic := '0';
+
+ signal hr_clock_phase165 : unsigned(1 downto 0) := "00";
+ signal hr_clock_phase : unsigned(2 downto 0) := "000";
+ signal hr_clock_phase_drive : unsigned(2 downto 0) := "111";
+
+ signal read_time_adjust : integer range 0 to 255 := 0;
+ signal seven_plus_read_time_adjust : unsigned(5 downto 0) := "000000";
+ signal thirtyone_plus_read_time_adjust : unsigned(5 downto 0) := "000000";
+ signal hyperram_access_address_read_time_adjusted : unsigned(5 downto 0) := "000000";
+
+ signal hyperram0_select : std_logic := '0';
+ signal hyperram1_select : std_logic := '0';
+ signal hyperram_access_address : unsigned(26 downto 0) := to_unsigned(0,27);
+
+ signal read_request_held : std_logic := '0';
+ signal write_request_held : std_logic := '0';
+ signal mark_cache_for_prefetch : std_logic := '0';
+
+ signal viciv_last_request_toggle : std_logic := '0';
+ signal viciv_bank : unsigned(7 downto 0) := x"00";
+ signal viciv_data_buffer : cache_row_t := (others => x"00");
+ signal viciv_buffer_toggle : std_logic := '0';
+ signal last_viciv_buffer_toggle : std_logic := '0';
+ signal viciv_next_byte : integer range 0 to 8 := 0;
+ signal viciv_request_count : unsigned(31 downto 0) := to_unsigned(0,32);
+ signal is_vic_fetch : boolean := false;
+ signal viciv_data_debug : std_logic := '0';
+ signal viciv_debug_priority : std_logic := '0';
+
+ signal read_request_latch : std_logic := '0';
+ signal read_request_delatch : std_logic := '0';
+ signal read_request_prev : std_logic := '0';
+ signal write_request_latch : std_logic := '0';
+ signal write_request_prev : std_logic := '0';
+
+ signal prefetch_when_idle : boolean := false;
+
+begin
+ process (pixelclock,clock163,clock325,hr_clk,hr_clk_phaseshift) is
+ variable clock_status_vector : unsigned(4 downto 0);
+ variable tempaddr : unsigned(26 downto 0);
+ variable show_cache0 : boolean := false;
+ variable show_cache1 : boolean := false;
+ variable show_collect0 : boolean := false;
+ variable show_collect1 : boolean := false;
+ variable show_block : boolean := false;
+ variable show_always : boolean := true;
+ begin
+ if rising_edge(pixelclock) then
+
+ invalidate_read_cache <= '0';
+ cache_row_update_address_changed <= '0';
+
+ if read_request='1' then
+ read_request_latch <= '1';
+ end if;
+ if write_request='1' then
+ write_request_latch <= '1';
+ end if;
+ if read_request_delatch = '1' then
+ read_request_latch <= '0';
+ end if;
+
+ -- Present the data to the VIC-IV
+ if viciv_data_debug = '1' then
+ viciv_data_strobe <= '1';
+ viciv_data_out <= viciv_request_count(7 downto 0);
+ else
+ viciv_data_strobe <= '0';
+ end if;
+ if viciv_buffer_toggle /= last_viciv_buffer_toggle then
+ report "VIC: Starting to send data";
+ last_viciv_buffer_toggle <= viciv_buffer_toggle;
+ viciv_data_out <= viciv_data_buffer(0);
+ report "VIC: Sending byte " & integer'image(0)
+ & " = $" & to_hstring(viciv_data_buffer(0));
+ viciv_next_byte <= 1;
+ viciv_data_strobe <= '1';
+ elsif viciv_next_byte < 8 then
+ report "VIC: Sending byte " & integer'image(viciv_next_byte)
+ & " = $" & to_hstring(viciv_data_buffer(viciv_next_byte));
+ viciv_data_out <= viciv_data_buffer(viciv_next_byte);
+ viciv_next_byte <= viciv_next_byte + 1;
+ viciv_data_strobe <= '1';
+ end if;
+
+ if in_simulation = true then
+ write_latency2 <= to_unsigned(5,8);
+ extra_write_latency2 <= to_unsigned(3,8);
+ end if;
+
+ report "read_request=" & std_logic'image(read_request)
+ & ", read_request_held=" & std_logic'image(read_request_held)
+ & ", write_request_held=" & std_logic'image(write_request_held)
+ & ", read_request_latch=" & std_logic'image(read_request_latch)
+ & ", write_request_latch=" & std_logic'image(write_request_latch)
+ & ", busy_internal=" & std_logic'image(busy_internal)
+ & ", write_request=" & std_logic'image(write_request)
+ & ", request_toggle(last) = " & std_logic'image(request_toggle) & "(" & std_logic'image(last_request_toggle) & ")."
+ & ", is_block_read=" & boolean'image(is_block_read)
+ & ", address=$" & to_hstring(address);
+
+ -- Pseudo random bits so that we can do randomised cache row replacement
+ if random_bits /= to_unsigned(251,8) then
+ random_bits <= random_bits + 1;
+ else
+ random_bits <= x"00";
+ end if;
+
+ -- Update short-circuit cache line
+ -- (We don't change validity, since we don't know if it is
+ -- valid or not).
+ -- This has to happen IMMEDIATELY so that slow_devices doesn't
+ -- accidentally read old data, while we are still scheduling the write.
+ if address(26 downto 3) = current_cache_line_address(26 downto 3) then
+ report "Requesting update of current_cache_line due to write. Value = $"
+ & to_hstring(wdata) & ", byte offset = " & integer'image(to_integer(address(2 downto 0)));
+ if wen_lo = '1' then
+ current_cache_line_update(to_integer(address(2 downto 0))) <= wdata;
+ current_cache_line_update_flags(to_integer(address(2 downto 0))) <=
+ not current_cache_line_update_flags(to_integer(address(2 downto 0)));
+ current_cache_line_update_address <= current_cache_line_address;
+ end if;
+ if wen_hi = '1' then
+ current_cache_line_update(to_integer(address(2 downto 0))+1) <= wdata_hi;
+ current_cache_line_update_flags(to_integer(address(2 downto 0))+1) <=
+ not current_cache_line_update_flags(to_integer(address(2 downto 0))+1);
+ current_cache_line_update_address <= current_cache_line_address;
+ end if;
+ end if;
+
+ if cache_enabled then
+ busy <= busy_internal or write_blocked or queued_write or queued2_write or (not start_delay_expired);
+ else
+ -- With no cache, we have to IMMEDIATELY assert busy when we see a
+ -- request to avoid a race-condition with slow_devices
+ busy <= busy_internal or write_blocked or queued_write or queued2_write
+ or read_request or write_request or read_request_latch or write_request_latch
+ or (not start_delay_expired);
+ end if;
+
+ if write_blocked = '1' and first_transaction='0' then
+-- report "DISPATCH: write_blocked asserted. Waiting for existing writes to flush...";
+ null;
+ end if;
+
+ -- Clear write block as soon as either write buffer clears
+ if (write_collect0_dispatchable='0' and write_collect0_toolate='0' and write_collect0_flushed='0')
+ or (write_collect1_dispatchable='0' and write_collect1_toolate='0' and write_collect1_flushed='0')
+ then
+ write_blocked <= queued_write or queued2_write;
+ else
+ write_blocked <= '1';
+ busy <= '1';
+ end if;
+
+ -- Similarly as soon as we see a VIC-IV request come through we need to
+ -- assert busy
+ if viciv_request_toggle /= viciv_last_request_toggle then
+ busy <= '1';
+ end if;
+
+ fake_data_ready_strobe <= '0';
+
+ if read_request = '1' or write_request = '1' or read_request_latch='1' or write_request_latch='1' then
+ request_counter_int <= not request_counter_int;
+ request_counter <= request_counter_int;
+ end if;
+
+ if cache_row0_address = cache_row1_address and cache_row0_address /= x"ffffff" then
+ report "ERROR: Cache row0 and row1 point to same address";
+ show_cache0 := true;
+ show_cache1 := true;
+ end if;
+
+
+
+ -- Clear write buffers once they have been flushed.
+ -- We have to wipe the address and valids, so that they don't get stuck being
+ -- used as stale sources for cache reading.
+ if write_collect0_dispatchable = '1' and write_collect0_toolate = '1' and write_collect0_flushed = '1' then
+ show_collect0 := true;
+ report "WRITE: Clearing collect0";
+ write_collect0_address <= (others => '1');
+ write_collect0_dispatchable <= '0';
+ end if;
+ if write_collect1_dispatchable = '1' and write_collect1_toolate = '1' and write_collect1_flushed = '1' then
+ if write_collect1_dispatchable='1' then
+ show_collect1 := true;
+ end if;
+ report "WRITE: Clearing collect1";
+ write_collect1_address <= (others => '1');
+ write_collect1_dispatchable <= '0';
+ end if;
+
+ if write_collect0_dispatchable = '0' and write_collect0_toolate = '0' and write_collect0_flushed = '0' then
+ if queued_write='1' then
+ report "DISPATCH: Dequeuing queued write to $" & to_hstring(queued_waddr);
+
+ -- Push it out as a normal batched write, that can collect others if they
+ -- come soon enough.
+
+ write_collect0_valids <= (others => '0');
+ if queued_wen_lo='1' then
+ write_collect0_valids(to_integer(queued_waddr(2 downto 0))) <= '1';
+ write_collect0_data(to_integer(queued_waddr(2 downto 0))) <= queued_wdata;
+ end if;
+ if queued_wen_hi='1' then
+ write_collect0_valids(to_integer(queued_waddr(2 downto 0))+1) <= '1';
+ write_collect0_data(to_integer(queued_waddr(2 downto 0))+1) <= queued_wdata_hi;
+ end if;
+ write_collect0_address <= queued_waddr(26 downto 3);
+ write_collect0_dispatchable <= '1';
+ show_collect0 := true;
+
+ queued_write <= '0';
+ elsif queued2_write='1' then
+ report "DISPATCH: Dequeuing queued write to $" & to_hstring(queued2_waddr);
+
+ -- Push it out as a normal batched write, that can collect others if they
+ -- come soon enough.
+
+ write_collect0_valids <= (others => '0');
+ if queued2_wen_lo='1' then
+ write_collect0_valids(to_integer(queued2_waddr(2 downto 0))) <= '1';
+ write_collect0_data(to_integer(queued2_waddr(2 downto 0))) <= queued2_wdata;
+ end if;
+ if queued2_wen_hi='1' then
+ write_collect0_valids(to_integer(queued2_waddr(2 downto 0))+1) <= '1';
+ write_collect0_data(to_integer(queued2_waddr(2 downto 0))+1) <= queued2_wdata_hi;
+ end if;
+ write_collect0_address <= queued2_waddr(26 downto 3);
+ write_collect0_dispatchable <= '1';
+ show_collect0 := true;
+
+ queued2_write <= '0';
+ end if;
+
+ end if;
+ if write_collect1_dispatchable = '0' and write_collect1_toolate = '0' and write_collect1_flushed = '0' then
+ if queued_write='1' then
+ report "DISPATCH: Dequeuing queued write to $" & to_hstring(queued_waddr);
+
+ -- Push it out as a normal batched write, that can collect others if they
+ -- come soon enough.
+
+ write_collect1_valids <= (others => '0');
+ if queued_wen_lo='1' then
+ write_collect1_valids(to_integer(queued_waddr(2 downto 0))) <= '1';
+ write_collect1_data(to_integer(queued_waddr(2 downto 0))) <= queued_wdata;
+ end if;
+ if queued_wen_hi='1' then
+ write_collect1_valids(to_integer(queued_waddr(2 downto 0))+1) <= '1';
+ write_collect1_data(to_integer(queued_waddr(2 downto 0))+1) <= queued_wdata_hi;
+ end if;
+ write_collect1_address <= queued_waddr(26 downto 3);
+ write_collect1_dispatchable <= '1';
+ show_collect1 := true;
+
+ queued_write <= '0';
+ end if;
+ end if;
+
+ -- Ignore read requests to the current block read, as they get
+ -- short-circuited in the inner state machine to save time.
+ report "address = $" & to_hstring(address);
+ if (read_request or read_request_latch)='1' and busy_internal='0'
+ and ((is_block_read = false) or block_address_matches_address='0')
+ -- Don't but in on the VIC-IV (but once we have submitted a request, we
+ -- do have priority)
+ and (viciv_last_request_toggle = viciv_request_toggle) then
+ report "Making read request for $" & to_hstring(address);
+ -- Begin read request
+
+ read_request_latch <= '0';
+
+ -- Check for cache read
+ -- We check the write buffers first, as any contents that they have
+ -- must take priority over everything else
+ if (block_valid='1') and (block_address_matches_address='1') then
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= block_data(to_integer(address(4 downto 3)))(to_integer(address(2 downto 0)));
+ if rdata_16en='1' then
+ fake_rdata_hi <= block_data(to_integer(address(4 downto 3)))(to_integer(address(2 downto 0))+1);
+ end if;
+ report "DISPATCH: Returning data $"
+ & to_hstring(block_data(to_integer(address(4 downto 3)))(to_integer(address(2 downto 0))))
+ & " from read block.";
+ -- Now update current cache line to speed up subsequent reads
+ current_cache_line_update <= block_data(to_integer(address(4 downto 3)));
+ current_cache_line_new_address <= address(26 downto 3);
+ current_cache_line_update_all <= not current_cache_line_update_all;
+
+ if (address(4 downto 3) = "11") and (flag_prefetch='1')
+ and (viciv_request_toggle = viciv_last_request_toggle) then
+ -- When attempting to read from the last 8 bytes of a block read,
+ -- we schedule a pre-fetch of the next 32 bytes, so that we can hide
+ -- the read latency as much as possible.
+ ram_reading <= '1';
+ tempaddr(26 downto 5) := address(26 downto 5) + 1;
+ tempaddr(4 downto 0) := "00000";
+ ram_address <= tempaddr;
+ request_toggle <= not request_toggle;
+ ram_prefetch <= true;
+ ram_normalfetch <= false;
+
+ report "DISPATCH: Dispatching pre-fetch of $" & to_hstring(tempaddr);
+ -- Mark a cache line to receive the pre-fetched data, so that we don't
+ -- have to wait for it all to turn up, before being able to return
+ -- the first 8 bytes
+ mark_cache_for_prefetch <= '1';
+ end if;
+
+ elsif cache_enabled and rdata_16en='0' and (address(26 downto 3 ) = write_collect0_address and write_collect0_valids(to_integer(address(2 downto 0))) = '1') then
+ -- Write cache read-back
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= write_collect0_data(to_integer(address(2 downto 0)));
+ report "DISPATCH: Returning data $"& to_hstring(write_collect0_data(to_integer(address(2 downto 0))))&" from write collect0";
+ elsif cache_enabled and rdata_16en='1' and (address(26 downto 3 ) = write_collect0_address
+ and write_collect0_valids(to_integer(address(2 downto 1)&"0")) = '1'
+ and write_collect0_valids(to_integer(address(2 downto 1)&"1")) = '1') then
+ -- Write cache read-back
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= write_collect0_data(to_integer(address(2 downto 1)&"0"));
+ fake_rdata_hi <= write_collect0_data(to_integer(address(2 downto 1)&"1"));
+ report "DISPATCH: Returning data $"& to_hstring(write_collect0_data(to_integer(address(2 downto 0))))&" from write collect0";
+ elsif cache_enabled and rdata_16en='0' and (address(26 downto 3 ) = write_collect1_address and write_collect1_valids(to_integer(address(2 downto 0))) = '1') then
+ -- Write cache read-back
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= write_collect1_data(to_integer(address(2 downto 0)));
+ report "DISPATCH: Returning data $"& to_hstring(write_collect1_data(to_integer(address(2 downto 0))))&" from write collect1";
+ elsif cache_enabled and rdata_16en='1' and (address(26 downto 3 ) = write_collect1_address
+ and write_collect1_valids(to_integer(address(2 downto 1)&"0")) = '1'
+ and write_collect1_valids(to_integer(address(2 downto 1)&"1")) = '1') then
+ -- Write cache read-back
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= write_collect1_data(to_integer(address(2 downto 1)&"0"));
+ fake_rdata_hi <= write_collect1_data(to_integer(address(2 downto 1)&"1"));
+ report "DISPATCH: Returning data $"& to_hstring(write_collect1_data(to_integer(address(2 downto 0))))&" from write collect1";
+ elsif cache_enabled and rdata_16en='0' and (address(26 downto 3 ) = cache_row0_address and cache_row0_valids(to_integer(address(2 downto 0))) = '1') then
+ -- Cache reads
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= cache_row0_data(to_integer(address(2 downto 0)));
+ report "DISPATCH: Returning data $"& to_hstring(cache_row0_data(to_integer(address(2 downto 0))))&" from cache row0";
+ elsif cache_enabled and rdata_16en='1' and (address(26 downto 3 ) = cache_row0_address
+ and cache_row0_valids(to_integer(address(2 downto 1)&"0")) = '1'
+ and cache_row0_valids(to_integer(address(2 downto 1)&"1")) = '1') then
+ -- Cache reads
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= cache_row0_data(to_integer(address(2 downto 1)&"0"));
+ fake_rdata_hi <= cache_row0_data(to_integer(address(2 downto 1)&"1"));
+ report "DISPATCH: Returning data $"& to_hstring(cache_row0_data(to_integer(address(2 downto 0))))&" from cache row0";
+ elsif cache_enabled and rdata_16en='0' and (address(26 downto 3 ) = cache_row1_address and cache_row1_valids(to_integer(address(2 downto 0))) = '1') then
+ -- Cache read
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= cache_row1_data(to_integer(address(2 downto 0)));
+ report "DISPATCH: Returning data $"& to_hstring(cache_row1_data(to_integer(address(2 downto 0))))&" from cache row1";
+ elsif cache_enabled and rdata_16en='1' and (address(26 downto 3 ) = cache_row1_address
+ and cache_row1_valids(to_integer(address(2 downto 1)&"0"))='1'
+ and cache_row1_valids(to_integer(address(2 downto 1)&"1"))='1') then
+
+ -- Cache read
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ fake_rdata <= cache_row1_data(to_integer(address(2 downto 1)&"0"));
+ fake_rdata_hi <= cache_row1_data(to_integer(address(2 downto 1)&"1"));
+ report "DISPATCH: Returning data $"& to_hstring(cache_row1_data(to_integer(address(2 downto 0))))&" from cache row1";
+ elsif address(23 downto 8) = x"00000" and address(25 downto 24) = "11" then
+ -- $B0000xx for now for debugging caches etc
+ case address(7 downto 0) is
+ when x"00" => fake_rdata(7) <= '1';
+ fake_rdata(6 downto 3) <= (others => '0');
+ fake_rdata(2 downto 0) <= cache_row0_address(23 downto 21);
+ when x"01" => fake_rdata <= cache_row0_address(20 downto 13);
+ when x"02" => fake_rdata <= cache_row0_address(12 downto 5);
+ when x"03" => fake_rdata(7 downto 3) <= cache_row0_address(4 downto 0);
+ fake_rdata(2 downto 0) <= "000";
+ when x"04" => fake_rdata <= unsigned(cache_row0_valids);
+ when x"05" => fake_rdata <= x"AA";
+ when x"06" => fake_rdata <= x"AA";
+ when x"07" => fake_rdata <= x"AA";
+ when x"08"|x"09"|x"0a"|x"0b"|x"0c"|x"0d"|x"0e"|x"0f" =>
+ fake_rdata <= cache_row0_data(to_integer(address(2 downto 0)));
+
+ when x"10" => fake_rdata(7) <= '1';
+ fake_rdata(6 downto 3) <= (others => '0');
+ fake_rdata(2 downto 0) <= cache_row1_address(23 downto 21);
+ when x"11" => fake_rdata <= cache_row1_address(20 downto 13);
+ when x"12" => fake_rdata <= cache_row1_address(12 downto 5);
+ when x"13" => fake_rdata(7 downto 3) <= cache_row1_address(4 downto 0);
+ fake_rdata(2 downto 0) <= "000";
+ when x"14" => fake_rdata <= unsigned(cache_row1_valids);
+ when x"15" => fake_rdata <= x"AA";
+ when x"16" => fake_rdata <= x"AA";
+ when x"17" => fake_rdata <= x"AA";
+ when x"18"
+ | x"19"
+ | x"1a"
+ | x"1b"
+ | x"1c"
+ | x"1d"
+ | x"1e"
+ | x"1f" => fake_rdata <= cache_row1_data(to_integer(address(2 downto 0)));
+
+ when x"20" => fake_rdata <= write_collect0_address(23 downto 16);
+ when x"21" => fake_rdata <= write_collect0_address(15 downto 8);
+ when x"22" => fake_rdata(7 downto 3) <= write_collect0_address(7 downto 3);
+ fake_rdata(2 downto 0) <= "000";
+ when x"23" => fake_rdata <= x"AA";
+ when x"24" => fake_rdata <= unsigned(write_collect0_valids);
+ when x"25" => fake_rdata <= x"AA";
+ when x"26" => fake_rdata <= x"00";
+ fake_rdata(4) <= write_collect0_dispatchable;
+ fake_rdata(1) <= write_collect0_toolate;
+ fake_rdata(0) <= write_collect0_flushed;
+ when x"27" => fake_rdata <= x"AA";
+ when x"28"
+ | x"29"
+ | x"2a"
+ | x"2b"
+ | x"2c"
+ | x"2d"
+ | x"2e"
+ | x"2f" => fake_rdata <= write_collect0_data(to_integer(address(2 downto 0)));
+
+ when x"30" => fake_rdata <= write_collect1_address(23 downto 16);
+ when x"31" => fake_rdata <= write_collect1_address(15 downto 8);
+ when x"32" => fake_rdata(7 downto 3) <= write_collect1_address(7 downto 3);
+ fake_rdata(2 downto 0) <= "000";
+ when x"33" => fake_rdata <= x"AA";
+ when x"34" => fake_rdata <= unsigned(write_collect1_valids);
+ when x"35" => fake_rdata <= x"AA";
+ when x"36" => fake_rdata <= x"00";
+ fake_rdata(4) <= write_collect1_dispatchable;
+ fake_rdata(1) <= write_collect1_toolate;
+ fake_rdata(0) <= write_collect1_flushed;
+ when x"37" => fake_rdata <= x"AA";
+ when x"38"
+ | x"39"
+ | x"3a"
+ | x"3b"
+ | x"3c"
+ | x"3d"
+ | x"3e"
+ | x"3f" => fake_rdata <= write_collect1_data(to_integer(address(2 downto 0)));
+
+ when x"40" => fake_rdata <= block_address(23 downto 16);
+ when x"41" => fake_rdata <= block_address(15 downto 8);
+ when x"42" => fake_rdata(7 downto 5) <= block_address(7 downto 5);
+ fake_rdata(4 downto 0) <= "00000";
+ when x"43" => fake_rdata <= x"AA";
+ when x"44" => fake_rdata <= x"00";
+ if (block_valid='1') then fake_rdata <= x"FF"; end if;
+
+ when x"50"
+ | x"51"
+ | x"52"
+ | x"53"
+ | x"54"
+ | x"55"
+ | x"56"
+ | x"57" => fake_rdata <= block_data(0)(to_integer(address(2 downto 0)));
+ when x"58"
+ | x"59"
+ | x"5a"
+ | x"5b"
+ | x"5c"
+ | x"5d"
+ | x"5e"
+ | x"5f" => fake_rdata <= block_data(1)(to_integer(address(2 downto 0)));
+
+
+ when x"60"
+ | x"61"
+ | x"62"
+ | x"63"
+ | x"64"
+ | x"65"
+ | x"66"
+ | x"67" => fake_rdata <= block_data(2)(to_integer(address(2 downto 0)));
+ when x"68"
+ | x"69"
+ | x"6a"
+ | x"6b"
+ | x"6c"
+ | x"6d"
+ | x"6e"
+ | x"6f" => fake_rdata <= block_data(3)(to_integer(address(2 downto 0)));
+
+ when x"80" => fake_rdata <= viciv_request_count(31 downto 24);
+ when x"81" => fake_rdata <= viciv_request_count(23 downto 16);
+ when x"82" => fake_rdata <= viciv_request_count(15 downto 8);
+ when x"83" => fake_rdata <= viciv_request_count( 7 downto 0);
+
+ when x"90" => fake_rdata(2 downto 0) <= current_cache_line_address_drive(26 downto 24);
+ fake_rdata(7) <= '1';
+ fake_rdata(6 downto 3) <= "0000";
+ when x"91" => fake_rdata <= current_cache_line_address_drive(23 downto 16);
+ when x"92" => fake_rdata <= current_cache_line_address_drive(15 downto 8);
+ when x"93" => fake_rdata(7 downto 3) <= current_cache_line_address_drive( 7 downto 3);
+ fake_rdata(2 downto 0) <= "000";
+ when x"94" => fake_rdata <= (others => current_cache_line_valid_drive);
+
+ when x"98"
+ | x"99"
+ | x"9a"
+ | x"9b"
+ | x"9c"
+ | x"9d"
+ | x"9e"
+ | x"9f" => fake_rdata <= current_cache_line_drive(to_integer(address(2 downto 0)));
+
+
+ when others => fake_rdata <= x"BF";
+ end case;
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ report "asserting data_ready_strobe for fake read";
+ elsif address(23 downto 4) = x"FFFFF" and address(25 downto 24) = "11" then
+ -- Allow reading from dummy debug bitbash registers at $BFFFFFx
+ case address(3 downto 0) is
+ when x"0" =>
+ fake_rdata <= to_unsigned(write_continues_max,8);
+ when x"1" =>
+ fake_rdata <= viciv_bank;
+ when x"2" =>
+ fake_rdata(0) <= fast_cmd_mode;
+ fake_rdata(1) <= fast_read_mode;
+ fake_rdata(2) <= fast_write_mode;
+ fake_rdata(3) <= read_phase_shift;
+ fake_rdata(4) <= block_read_enable;
+ fake_rdata(5) <= flag_prefetch;
+ fake_rdata(6) <= enable_current_cache_line;
+ if cache_enabled then
+ fake_rdata(7) <= '1';
+ else
+ fake_rdata(7) <= '0';
+ end if;
+ when x"3" =>
+ fake_rdata <= write_latency;
+ when x"4" =>
+ fake_rdata <= extra_write_latency;
+ when x"5" =>
+ fake_rdata <= to_unsigned(read_time_adjust,8);
+ when x"6" =>
+ fake_rdata <= rwr_delay;
+ when x"7" =>
+ fake_rdata <= unsigned(cache_row0_valids);
+ when x"8" =>
+ fake_rdata <= conf_buf0;
+ when x"9" =>
+ fake_rdata <= conf_buf1;
+
+ when x"a" =>
+ fake_rdata <= cache_row0_address(7 downto 0);
+ when x"b" =>
+ fake_rdata <= cache_row0_address(15 downto 8);
+ when x"c" =>
+ fake_rdata <= cache_row0_address(23 downto 16);
+
+ when x"d" =>
+ fake_rdata <= write_latency2;
+ when x"e" =>
+ fake_rdata <= extra_write_latency2;
+ when x"f" =>
+ fake_rdata <= x"00";
+ fake_rdata(0) <= viciv_data_debug;
+ fake_rdata(1) <= viciv_debug_priority;
+ when others =>
+ -- This seems to be what gets returned all the time
+ fake_rdata <= x"42";
+ end case;
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ report "asserting data_ready_strobe for fake read";
+ elsif request_accepted = request_toggle then
+ -- Normal RAM read.
+ report "request_toggle flipped";
+ ram_reading <= '1';
+ ram_address <= address;
+ ram_normalfetch <= true;
+ -- We just need to check if there is a pre-fetch already
+ -- queued for this same address
+ if address(26 downto 3) = ram_address(26 downto 3) and ram_prefetch then
+ report "DISPATCH: Merging read with pre-fetch.";
+ else
+ report "DISPATCH: Cancelling pre-fetch to prioritise explicit read";
+ ram_prefetch <= false;
+ end if;
+ request_toggle <= not request_toggle;
+ end if;
+ elsif queued_write='1' and write_collect0_dispatchable='0' and write_collect0_flushed='0'
+ and write_collect0_toolate='0' then
+
+ report "DISPATCH: Executing queued write to $" & to_hstring(queued_waddr);
+
+ -- Push it out as a normal batched write, that can collect others if they
+ -- come soon enough.
+
+ write_collect0_valids <= (others => '0');
+ if queued_wen_lo='1' then
+ write_collect0_valids(to_integer(queued_waddr(2 downto 0))) <= '1';
+ write_collect0_data(to_integer(queued_waddr(2 downto 0))) <= queued_wdata;
+ end if;
+ if queued_wen_hi='1' then
+ write_collect0_valids(to_integer(queued_waddr(2 downto 0))+1) <= '1';
+ write_collect0_data(to_integer(queued_waddr(2 downto 0))+1) <= queued_wdata_hi;
+ end if;
+ write_collect0_address <= queued_waddr(26 downto 3);
+ write_collect0_dispatchable <= '1';
+ show_collect0 := true;
+
+ queued_write <= '0';
+
+ elsif (write_request or write_request_latch)='1' and busy_internal='0' then
+ report "Making write request: addr $" & to_hstring(address) & " <= " & to_hstring(wdata);
+ -- Begin write request
+ -- Latch address and data
+
+ write_request_latch <= '0';
+
+ if address(23 downto 4) = x"FFFFF" and address(25 downto 24) = "11" then
+ case address(3 downto 0) is
+ when x"0" =>
+ write_continues_max <= to_integer(wdata);
+ when x"1" =>
+ viciv_bank <= wdata;
+ when x"2" =>
+ fast_cmd_mode <= wdata(0);
+ fast_read_mode <= wdata(1);
+ fast_write_mode <= wdata(2);
+ read_phase_shift <= wdata(3);
+ block_read_enable <= wdata(4);
+ flag_prefetch <= wdata(5);
+ enable_current_cache_line <= wdata(6);
+ if wdata(7)='1' then
+ cache_enabled <= true;
+ else
+ cache_enabled <= false;
+ end if;
+ when x"3" =>
+ write_latency <= wdata;
+ when x"4" =>
+ extra_write_latency <= wdata;
+ when x"5" =>
+ read_time_adjust <= to_integer(wdata);
+ when x"6" =>
+ rwr_delay <= wdata;
+ when x"8" =>
+ conf_buf0_in <= wdata;
+ conf_buf0_set <= not conf_buf0_set;
+ when x"9" =>
+ conf_buf1_in <= wdata;
+ conf_buf1_set <= not conf_buf1_set;
+ when x"d" =>
+ write_latency2 <= wdata;
+ when x"e" =>
+ extra_write_latency2 <= wdata;
+ when x"f" =>
+ viciv_data_debug <= wdata(0);
+ viciv_debug_priority <= wdata(1);
+ when others =>
+ null;
+ end case;
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ else
+ -- Always do cached writes, as apart from the latency before
+ -- they get written out, it seems to be pretty reliable
+ -- if cache_enabled = false then
+ if false then
+ -- Do normal write request
+ report "request_toggle flipped";
+ report "DISPATCH: Accepted non-cached write";
+ ram_prefetch <= false;
+ ram_normalfetch <= true;
+ request_toggle <= not request_toggle;
+ ram_reading <= '0';
+ ram_address <= address;
+ ram_wdata <= wdata;
+ ram_wdata_hi <= wdata_hi;
+ ram_wdata_enlo <= wen_lo;
+ ram_wdata_enhi <= wen_hi;
+ else
+ -- Collect writes together for dispatch
+
+ -- Can we add the write to an existing collected write?
+ if write_collect0_toolate = '0' and write_collect0_address = address(26 downto 3)
+ and write_collect0_dispatchable = '1' and write_collect0_toolate='0'
+ then
+ if wen_lo='1' then
+ write_collect0_valids(to_integer(address(2 downto 0))) <= '1';
+ write_collect0_data(to_integer(address(2 downto 0))) <= wdata;
+ end if;
+ if wen_hi='1' then
+ write_collect0_valids(to_integer(address(2 downto 0))+1) <= '1';
+ write_collect0_data(to_integer(address(2 downto 0))+1) <= wdata_hi;
+ end if;
+ show_collect0 := true;
+ elsif write_collect1_toolate = '0' and write_collect1_address = address(26 downto 3)
+ and write_collect1_dispatchable = '1' and write_collect1_toolate='0' then
+ if wen_lo='1' then
+ write_collect1_valids(to_integer(address(2 downto 0))) <= '1';
+ write_collect1_data(to_integer(address(2 downto 0))) <= wdata;
+ end if;
+ if wen_hi='1' then
+ write_collect1_valids(to_integer(address(2 downto 0))+1) <= '1';
+ write_collect1_data(to_integer(address(2 downto 0))+1) <= wdata_hi;
+ end if;
+ show_collect1 := true;
+ elsif write_collect0_dispatchable = '0' and write_collect0_toolate='0' then
+ write_collect0_valids <= (others => '0');
+ if wen_lo='1' then
+ write_collect0_valids(to_integer(address(2 downto 0))) <= '1';
+ write_collect0_data(to_integer(address(2 downto 0))) <= wdata;
+ end if;
+ if wen_hi='1' then
+ write_collect0_valids(to_integer(address(2 downto 0))+1) <= '1';
+ write_collect0_data(to_integer(address(2 downto 0))+1) <= wdata_hi;
+ end if;
+
+ write_collect0_address <= address(26 downto 3);
+ write_collect0_dispatchable <= '1';
+ -- Block further writes if we already have one busy write buffer
+ write_blocked <= '1';
+ show_collect0 := true;
+ elsif write_collect1_dispatchable = '0' and write_collect1_toolate='0' then
+ write_collect1_valids <= (others => '0');
+ if wen_lo='1' then
+ write_collect1_valids(to_integer(address(2 downto 0))) <= '1';
+ write_collect1_data(to_integer(address(2 downto 0))) <= wdata;
+ end if;
+ if wen_hi='1' then
+ write_collect1_valids(to_integer(address(2 downto 0))+1) <= '1';
+ write_collect1_data(to_integer(address(2 downto 0))+1) <= wdata_hi;
+ end if;
+ write_collect1_address <= address(26 downto 3);
+ write_collect1_dispatchable <= '1';
+ -- Block further writes if we already have one busy write buffer
+ write_blocked <= '1';
+ show_collect1 := true;
+ else
+ -- No write collection point that we can use, so just block until
+ -- one becomes available
+ report "DISPATCH: Write blocked due to busy write buffers: " &
+ " addr $" & to_hstring(address) & " <= " & to_hstring(wdata);
+ if queued_write='1' then
+ -- Bother. We already had a queued write.
+ -- So remember that one, too
+ report "Stashing in queued2";
+ queued2_waddr <= address;
+ queued2_wdata <= wdata;
+ queued2_wdata_hi <= wdata_hi;
+ queued2_wen_lo <= wen_lo;
+ queued2_wen_hi <= wen_hi;
+ queued2_write <= '1';
+ else
+ report "Stashing in queued";
+ queued_waddr <= address;
+ queued_wdata <= wdata;
+ queued_wdata_hi <= wdata_hi;
+ queued_wen_lo <= wen_lo;
+ queued_wen_hi <= wen_hi;
+ queued_write <= '1';
+ end if;
+
+ end if;
+
+ -- Update read cache structures when writing
+ report "CACHE: Requesting update of cache due to write: $" & to_hstring(address) & " = $" & to_hstring(wdata);
+ cache_row_update_address <= address(26 downto 3);
+ cache_row_update_address_changed <= '1';
+ cache_row_update_byte <= to_integer(address(2 downto 0));
+ cache_row_update_value <= wdata;
+ cache_row_update_value_hi <= wdata_hi;
+ cache_row_update_lo <= wen_lo;
+ cache_row_update_hi <= wen_hi;
+ if cache_row_update_toggle = not last_cache_row_update_toggle then
+ -- At least one other cache update is pending. This means that
+ -- we will not be able to keep the cache consistent. The only
+ -- option is to invalidate the read cache rows, data block,
+ -- and current_cache_line.
+ invalidate_read_cache <= '1';
+ else
+ cache_row_update_toggle <= not last_cache_row_update_toggle;
+ end if;
+
+ end if;
+ end if;
+ else
+ -- Nothing new to do
+ if data_ready_toggle /= last_data_ready_toggle then
+ last_data_ready_toggle <= data_ready_toggle;
+ report "asserting fake_data_ready_strobe";
+ fake_data_ready_strobe <= '1';
+ end if;
+ end if;
+
+ end if;
+ -- Optionally delay HR_CLK by 1/2 an 160MHz clock cycle
+ -- (actually just by optionally inverting it)
+ if rising_edge(clock325) then
+
+ if show_cache0 or show_always then
+ report "cache_row0_address_matches_cache_row_update_address="
+ & std_logic'image(cache_row0_address_matches_cache_row_update_address)
+ & ", cache_row_update_address=$" & to_hstring(cache_row_update_address&"000");
+ report "CACHE cache0: address=$" & to_hstring(cache_row0_address&"000") & ", valids=" & to_string(cache_row0_valids)
+ & ", data = "
+ & to_hstring(cache_row0_data(0)) & " "
+ & to_hstring(cache_row0_data(1)) & " "
+ & to_hstring(cache_row0_data(2)) & " "
+ & to_hstring(cache_row0_data(3)) & " "
+ & to_hstring(cache_row0_data(4)) & " "
+ & to_hstring(cache_row0_data(5)) & " "
+ & to_hstring(cache_row0_data(6)) & " "
+ & to_hstring(cache_row0_data(7)) & " ";
+ show_cache0 := false;
+ end if;
+
+ if show_cache1 or show_always then
+ report "CACHE cache1: address=$" & to_hstring(cache_row1_address&"000") & ", valids=" & to_string(cache_row1_valids)
+ & ", data = "
+ & to_hstring(cache_row1_data(0)) & " "
+ & to_hstring(cache_row1_data(1)) & " "
+ & to_hstring(cache_row1_data(2)) & " "
+ & to_hstring(cache_row1_data(3)) & " "
+ & to_hstring(cache_row1_data(4)) & " "
+ & to_hstring(cache_row1_data(5)) & " "
+ & to_hstring(cache_row1_data(6)) & " "
+ & to_hstring(cache_row1_data(7)) & " ";
+ show_cache1 := false;
+ end if;
+ if show_collect0 or show_always then
+ report "CACHE write0: $" & to_hstring(write_collect0_address&"000") & ", v=" & to_string(write_collect0_valids)
+ & ", d=" & std_logic'image(write_collect0_dispatchable)
+ & ", late=" & std_logic'image(write_collect0_toolate)
+ & ", fl=" & std_logic'image(write_collect0_flushed)
+ & ", data = "
+ & to_hstring(write_collect0_data(0)) & " "
+ & to_hstring(write_collect0_data(1)) & " "
+ & to_hstring(write_collect0_data(2)) & " "
+ & to_hstring(write_collect0_data(3)) & " "
+ & to_hstring(write_collect0_data(4)) & " "
+ & to_hstring(write_collect0_data(5)) & " "
+ & to_hstring(write_collect0_data(6)) & " "
+ & to_hstring(write_collect0_data(7)) & " ";
+ show_collect0 := false;
+ end if;
+ if show_collect1 or show_always then
+ report "CACHE write1: $" & to_hstring(write_collect1_address&"000") & ", v=" & to_string(write_collect1_valids)
+ & ", d=" & std_logic'image(write_collect1_dispatchable)
+ & ", late=" & std_logic'image(write_collect1_toolate)
+ & ", fl=" & std_logic'image(write_collect1_flushed)
+ & ", data = "
+ & to_hstring(write_collect1_data(0)) & " "
+ & to_hstring(write_collect1_data(1)) & " "
+ & to_hstring(write_collect1_data(2)) & " "
+ & to_hstring(write_collect1_data(3)) & " "
+ & to_hstring(write_collect1_data(4)) & " "
+ & to_hstring(write_collect1_data(5)) & " "
+ & to_hstring(write_collect1_data(6)) & " "
+ & to_hstring(write_collect1_data(7)) & " ";
+ show_collect1 := false;
+ end if;
+ if show_block or show_always then
+ report "CACHE block0: $" & to_hstring(block_address&"00000") & ", valid=" & std_logic'image(block_valid)
+ & ", byte_phase=" & integer'image(to_integer(byte_phase));
+ for i in 0 to 3 loop
+ report "CACHE block0 segment " & integer'image(i) & ": "
+ & to_hstring(block_data(i)(0)) & " "
+ & to_hstring(block_data(i)(1)) & " "
+ & to_hstring(block_data(i)(2)) & " "
+ & to_hstring(block_data(i)(3)) & " "
+ & to_hstring(block_data(i)(4)) & " "
+ & to_hstring(block_data(i)(5)) & " "
+ & to_hstring(block_data(i)(6)) & " "
+ & to_hstring(block_data(i)(7)) & " ";
+ end loop;
+ show_block := false;
+ end if;
+
+
+ hr_clock_phase_drive <= hr_clock_phase;
+ hr_clock_phase <= hr_clock_phase + 1;
+ -- Changing at the end of a phase cycle prevents us having any
+ -- problematically short clock pulses when it matters.
+ if hr_clock_phase_drive="110" then
+ hr_clk_fast_current <= hr_clk_fast;
+ hr_clk_phaseshift_current <= hr_clk_phaseshift;
+ if hr_clk_fast /= hr_clk_fast_current or hr_clk_phaseshift_current /= hr_clk_phaseshift then
+ report "Updating hr_clock_fast to " & std_logic'image(hr_clk_fast)
+ & ", hr_clk_phaseshift to " & std_logic'image(hr_clk_phaseshift);
+ end if;
+ end if;
+
+ -- Only change clock mode when safe to do so
+ clock_status_vector(4) := hr_clk_fast_current;
+ clock_status_vector(3) := hr_clk_phaseshift_current;
+ clock_status_vector(2 downto 0) := hr_clock_phase;
+ report "clock phase vector = " & to_string(std_logic_vector(clock_status_vector));
+ case clock_status_vector is
+ -- Slow clock rate, no phase shift
+ when "00000" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "00001" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "00010" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "00011" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "00100" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "00101" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "00110" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "00111" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+
+ -- Slow clock rate, with phase shift = bring forward tick by 1/2 a cycle
+ when "01000" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "01001" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "01010" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "01011" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "01100" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "01101" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "01110" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "01111" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+
+ -- Fast clock rate, no phase shift
+ when "10000" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "10001" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "10010" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "10011" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "10100" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "10101" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "10110" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "10111" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+
+ -- Fast clock rate, with phase shift
+ when "11000" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "11001" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "11010" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "11011" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "11100" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ when "11101" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "11110" => hr_clk <= '1'; hr_clk_p <= '1'; hr_clk_n <= '0';
+ when "11111" => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+
+ when others => hr_clk <= '0'; hr_clk_p <= '0'; hr_clk_n <= '1';
+ end case;
+
+ end if;
+
+ if rising_edge(clock163) then
+ hr_clock_phase165 <= hr_clock_phase165 + 1;
+
+ cycle_count <= cycle_count + 1;
+
+ if read_request_delatch='1' and read_request_latch='0' then
+ read_request_delatch <= '0';
+ end if;
+
+ mark_cache_for_prefetch <= '0';
+
+ hyperram_access_address_read_time_adjusted <= to_unsigned(to_integer(hyperram_access_address(2 downto 0))+read_time_adjust,6);
+ seven_plus_read_time_adjust <= to_unsigned(7 + read_time_adjust,6);
+ thirtyone_plus_read_time_adjust <= to_unsigned(31 + read_time_adjust,6);
+
+ -- We run double the clock speed of the pixelclock area, so no request
+ -- can come in during the extra drive cycle we use to update these values
+ -- so as to improve the timing closure of the whole thing
+ ram_wdata_drive <= ram_wdata;
+ ram_wdata_hi_drive <= ram_wdata_hi;
+ ram_address_drive <= ram_address;
+ ram_reading_drive <= ram_reading;
+ ram_wdata_enlo_drive <= ram_wdata_enlo;
+ ram_wdata_enhi_drive <= ram_wdata_enhi;
+ if ram_address(26 downto 3) = current_cache_line_address(26 downto 3) then
+ ram_address_matches_current_cache_line_address <= '1';
+ else
+ ram_address_matches_current_cache_line_address <= '0';
+ end if;
+ if cache_row0_address = ram_address(26 downto 3) then
+ cache_row0_address_matches_ram_address <= '1';
+ else
+ cache_row0_address_matches_ram_address <= '0';
+ end if;
+ if cache_row1_address = ram_address(26 downto 3) then
+ cache_row1_address_matches_ram_address <= '1';
+ else
+ cache_row1_address_matches_ram_address <= '0';
+ end if;
+ if write_collect0_address = background_write_next_address then
+ background_write_next_address_matches_collect0 <= '1';
+ else
+ background_write_next_address_matches_collect0 <= '0';
+ end if;
+ if write_collect1_address = background_write_next_address then
+ background_write_next_address_matches_collect1 <= '1';
+ else
+ background_write_next_address_matches_collect1 <= '0';
+ end if;
+ if write_collect1_address = write_collect0_address + 1 then
+ collect1_matches_collect0_plus_1 <= '1';
+ else
+ collect1_matches_collect0_plus_1 <= '0';
+ end if;
+ if write_collect0_address = write_collect1_address + 1 then
+ collect0_matches_collect1_plus_1 <= '1';
+ else
+ collect0_matches_collect1_plus_1 <= '0';
+ end if;
+
+
+ if address(26 downto 5) = block_address then
+ block_address_matches_address <= '1';
+ else
+ block_address_matches_address <= '0';
+ end if;
+
+ if address(26 downto 5) = hyperram_access_address(26 downto 5) then
+ address_matches_hyperram_access_address_block <= '1';
+ else
+ address_matches_hyperram_access_address_block <= '0';
+ end if;
+ if read_request='1' or read_request_held='1' then
+ read_request_prev <= '1';
+ else
+ read_request_prev <= '0';
+ end if;
+ if write_request='1' or write_request_held='1' then
+ write_request_prev <= '1';
+ else
+ write_request_prev <= '0';
+ end if;
+
+ if write_collect0_address = (write_collect1_address + 1) then
+ write_collect0_address_matches_write_collect1_address_plus_1 <= '1';
+ else
+ write_collect0_address_matches_write_collect1_address_plus_1 <= '0';
+ end if;
+
+ if to_integer(byte_phase) > to_integer(address(4 downto 0)) then
+ byte_phase_greater_than_address_low_bits <= '1';
+ else
+ byte_phase_greater_than_address_low_bits <= '0';
+ end if;
+ if to_integer(byte_phase) > to_integer(address(4 downto 3)&"111") then
+ byte_phase_greater_than_address_end_of_row <= '1';
+ else
+ byte_phase_greater_than_address_end_of_row <= '0';
+ end if;
+
+
+ -- Update short-circuit cache line
+ -- (We don't change validity, since we don't know if it is
+ -- valid or not).
+ if ram_address_matches_current_cache_line_address = '1' then
+ if ram_wdata_enlo_drive='1' then
+ current_cache_line_drive(to_integer(hyperram_access_address(2 downto 0))) <= ram_wdata_drive;
+ end if;
+ if ram_wdata_enhi_drive='1' then
+ current_cache_line_drive(to_integer(hyperram_access_address(2 downto 0))+1) <= ram_wdata_hi_drive;
+ end if;
+ end if;
+
+ if current_cache_line_address(26 downto 5) = block_address(26 downto 5)
+ and (current_cache_line_address(4 downto 3) /= "11") and (block_valid='1') then
+ current_cache_line_matches_block <= '1';
+ else
+ current_cache_line_matches_block <= '0';
+ end if;
+ if (current_cache_line_address(26 downto 5) + 1) = block_address(26 downto 5)
+ and (current_cache_line_address(4 downto 3) = "11") and (block_valid='1') then
+ current_cache_line_plus_1_matches_block <= '1';
+ else
+ current_cache_line_plus_1_matches_block <= '0';
+ end if;
+
+ if cache_row0_address = hyperram_access_address(26 downto 3) then
+ hyperram_access_address_matches_cache_row0 <= '1';
+ else
+ hyperram_access_address_matches_cache_row0 <= '0';
+ end if;
+ if cache_row1_address = hyperram_access_address(26 downto 3) then
+ hyperram_access_address_matches_cache_row1 <= '1';
+ else
+ hyperram_access_address_matches_cache_row1 <= '0';
+ end if;
+ if cache_row0_address = cache_row_update_address then
+ cache_row0_address_matches_cache_row_update_address <= '1';
+ else
+ cache_row0_address_matches_cache_row_update_address <= '0';
+ end if;
+ if cache_row1_address = cache_row_update_address then
+ cache_row1_address_matches_cache_row_update_address <= '1';
+ else
+ cache_row1_address_matches_cache_row_update_address <= '0';
+ end if;
+ if block_address = cache_row_update_address(26 downto 5) then
+ block_address_matches_cache_row_update_address <= '1';
+ else
+ block_address_matches_cache_row_update_address <= '0';
+ end if;
+
+
+ if enable_current_cache_line='1' then
+-- if current_cache_line /= current_cache_line_drive then
+-- report "CACHE: Updating current_cache_line from drive. Now "
+-- & to_hstring(current_cache_line_drive(0)) & " ...";
+-- end if;
+ current_cache_line <= current_cache_line_drive;
+ current_cache_line_address <= current_cache_line_address_drive;
+ current_cache_line_valid <= current_cache_line_valid_drive;
+ end if;
+
+ if mark_cache_for_prefetch='1' then
+ if random_bits(1)='0' then
+ report "Zeroing cache_row0_valids";
+ cache_row0_valids <= (others => '0');
+ cache_row0_address <= ram_address(26 downto 3);
+ cache_row0_address_matches_ram_address <= '1';
+ show_cache0 := true;
+ else
+ report "Zeroing cache_row1_valids";
+ cache_row1_valids <= (others => '0');
+ cache_row1_address <= ram_address(26 downto 3);
+ cache_row1_address_matches_ram_address <= '1';
+ show_cache1 := true;
+ end if;
+ end if;
+
+ if data_ready_strobe_hold = '0' then
+ if fake_data_ready_strobe='1' then
+ report "asserting data_ready_strobe via fake_data_ready_strobe";
+ end if;
+ data_ready_strobe <= fake_data_ready_strobe;
+ if fake_data_ready_strobe='1' then
+ report "DISPATCH: holding data_ready_strobe via fake data = $" & to_hstring(fake_rdata);
+ rdata <= fake_rdata;
+ rdata_hi <= fake_rdata_hi;
+ end if;
+ else
+ report "holding data_ready_strobe for an extra cycle";
+ report "asserting data_ready_strobe";
+ data_ready_strobe <= '1';
+ end if;
+ data_ready_strobe_hold <= '0';
+
+ -- HyperRAM state machine
+ report "State = " & state_t'image(state) & " @ Cycle " & integer'image(cycle_count)
+ & ", config_reg_write=" & std_logic'image(config_reg_write);
+
+ if conf_buf0_set /= last_conf_buf0_set then
+ last_conf_buf0_set <= conf_buf0_set;
+ conf_buf0 <= conf_buf0_in;
+ end if;
+ if conf_buf1_set /= last_conf_buf1_set then
+ last_conf_buf1_set <= conf_buf1_set;
+ conf_buf1 <= conf_buf1_in;
+ end if;
+
+
+ -- Invalidate cache if disabled
+ if cache_enabled = false then
+ report "Zeroing cache_row0_valids";
+ cache_row0_valids <= (others => '0');
+ cache_row1_valids <= (others => '0');
+ current_cache_line_valid_drive <= '0';
+ block_valid <= '0';
+ end if;
+
+ if current_cache_line_update_all = last_current_cache_line_update_all then
+ if current_cache_line_update_address = current_cache_line_address_drive then
+ for i in 0 to 7 loop
+ if current_cache_line_update_flags(i) /= last_current_cache_line_update_flags(i) then
+ report "CACHE: Driving update to current_cache_line byte " & integer'image(i)
+ & ", value $" & to_hstring(current_cache_line_update(i));
+ last_current_cache_line_update_flags(i) <= current_cache_line_update_flags(i);
+ current_cache_line_drive(i) <= current_cache_line_update(i);
+ end if;
+ end loop;
+ else
+ report "CACHE: Rejecting stale current line updates for $" & to_hstring(current_cache_line_update_address&"000");
+ end if;
+ else
+ report "DISPATCHER: Replacing current cache line with $" & to_hstring(current_cache_line_new_address&"000");
+ last_current_cache_line_update_all <= current_cache_line_update_all;
+ current_cache_line_address_drive <= current_cache_line_new_address;
+ current_cache_line_drive <= current_cache_line_update;
+ current_cache_line_update_flags <= last_current_cache_line_update_flags;
+ end if;
+
+ -- See if slow_devices is asking for the next 8 bytes.
+ -- If we have it, then pre-present it if we have it in our data block
+ -- (If it isn't in the data block, then we will have presumably already
+ -- started a pre-fetch when we serviced the access that created the current
+ -- data value in the current cache line entry.
+ -- This has to happen below the above single-byte update stuff, so that
+ -- we end retain cache coherency
+ if expansionram_current_cache_line_next_toggle /= last_current_cache_next_toggle then
+ last_current_cache_next_toggle <= expansionram_current_cache_line_next_toggle;
+ if current_cache_line_plus_1_matches_block = '1'
+ then
+ report "DISPATCHER: Presenting next 8 bytes to slow_devices. Was $"
+ & to_hstring(current_cache_line_address&"000") & ", new is $"
+ & to_hstring(current_cache_line_address(26 downto 5)&(current_cache_line_address(4 downto 3) + 1)&"000");
+ current_cache_line_address_drive(26 downto 5) <= block_address;
+ current_cache_line_address_drive(4 downto 3) <= "00";
+ current_cache_line_drive <= block_data(0);
+ current_cache_line_valid_drive <= '1';
+ -- Cancel any other updates that might be scheduled for this
+ last_current_cache_line_update_all <= current_cache_line_update_all;
+ last_current_cache_line_update_flags <= current_cache_line_update_flags;
+ end if;
+ if current_cache_line_matches_block = '1'
+ then
+ report "DISPATCHER: Presenting next 8 bytes to slow_devices. Was $"
+ & to_hstring(current_cache_line_address&"000") & ", new is $"
+ & to_hstring(current_cache_line_address(26 downto 5)&(current_cache_line_address(4 downto 3) + 1)&"000")
+ & ", data is:"
+ & " " & to_hstring(block_data(to_integer(current_cache_line_address(4 downto 3)) + 1)(0))
+ & " " & to_hstring(block_data(to_integer(current_cache_line_address(4 downto 3)) + 1)(1))
+ & " " & to_hstring(block_data(to_integer(current_cache_line_address(4 downto 3)) + 1)(2))
+ & " " & to_hstring(block_data(to_integer(current_cache_line_address(4 downto 3)) + 1)(3))
+ & " " & to_hstring(block_data(to_integer(current_cache_line_address(4 downto 3)) + 1)(4))
+ & " " & to_hstring(block_data(to_integer(current_cache_line_address(4 downto 3)) + 1)(5))
+ & " " & to_hstring(block_data(to_integer(current_cache_line_address(4 downto 3)) + 1)(6))
+ & " " & to_hstring(block_data(to_integer(current_cache_line_address(4 downto 3)) + 1)(7));
+ current_cache_line_address_drive(4 downto 3) <= current_cache_line_address(4 downto 3) + 1;
+ current_cache_line_drive <= block_data(to_integer(current_cache_line_address(4 downto 3)) + 1);
+ current_cache_line_valid_drive <= '1';
+ -- Cancel any other updates that might be scheduled for this
+ last_current_cache_line_update_all <= current_cache_line_update_all;
+ last_current_cache_line_update_flags <= current_cache_line_update_flags;
+
+ -- If it was the last row in the block that we have just presented,
+ -- it would be a really good idea to dispatch a pre-fetch right now.
+ -- The trick is that we can only safely do this, if we are idle.
+ if current_cache_line_address(4 downto 3) = "10" and flag_prefetch='1' then
+ report "DISPATCHER: Queuing chained pre-fetch";
+ prefetch_when_idle <= true;
+ end if;
+
+ end if;
+ end if;
+
+ -- Keep read request when required
+ read_request_held <= read_request;
+ write_request_held <= write_request;
+
+ if start_delay_expired='0' then
+ start_delay_counter <= start_delay_counter - 1;
+ if start_delay_counter = 0 then
+ start_delay_expired <= '1';
+ state <= WriteSetup;
+ end if;
+ end if;
+
+-- report "CACHE: row update status: requested = " & boolean'image(cache_row_update_toggle /= last_cache_row_update_toggle)
+-- & ", cache_row_update_address_changed = " & std_logic'image(cache_row_update_address_changed);
+
+ if cache_row_update_toggle /= last_cache_row_update_toggle and cache_row_update_address_changed = '0' then
+ if cache_row0_address_matches_cache_row_update_address = '1' then
+ if cache_row_update_lo='1' then
+ report "DISPATCH: Updating cache0 via write: $" & to_hstring((cache_row_update_address&"000")+cache_row_update_byte)
+ & " gets $" & to_hstring(cache_row_update_value);
+ cache_row0_valids(cache_row_update_byte) <= '1';
+ cache_row0_data(cache_row_update_byte) <= cache_row_update_value;
+ end if;
+ if cache_row_update_hi='1' then
+ report "DISPATCH: Updating cache0 via write: $" & to_hstring((cache_row_update_address&"001")+cache_row_update_byte)
+ & " gets $" & to_hstring(cache_row_update_value_hi);
+ cache_row0_valids(cache_row_update_byte+1) <= '1';
+ cache_row0_data(cache_row_update_byte+1) <= cache_row_update_value_hi;
+ end if;
+ show_cache0 := true;
+ end if;
+ if cache_row1_address_matches_cache_row_update_address = '1' then
+ if cache_row_update_lo='1' then
+ report "DISPATCH: Updating cache1 via write: $" & to_hstring((cache_row_update_address&"000")+cache_row_update_byte)
+ & " gets $" & to_hstring(cache_row_update_value);
+ cache_row1_valids(cache_row_update_byte) <= '1';
+ cache_row1_data(cache_row_update_byte) <= cache_row_update_value;
+ end if;
+ if cache_row_update_hi='1' then
+ report "DISPATCH: Updating cache1 via write: $" & to_hstring((cache_row_update_address&"001")+cache_row_update_byte)
+ & " gets $" & to_hstring(cache_row_update_value);
+ cache_row1_valids(cache_row_update_byte+1) <= '1';
+ cache_row1_data(cache_row_update_byte+1) <= cache_row_update_value_hi;
+ end if;
+ show_cache1 := true;
+ end if;
+ if block_address_matches_cache_row_update_address = '1' then
+ if cache_row_update_lo='1' then
+ report "DISPATCH: Updating block data via write: $" & to_hstring((cache_row_update_address&"000")+cache_row_update_byte)
+ & " gets $" & to_hstring(cache_row_update_value);
+ block_data(to_integer(cache_row_update_address(4 downto 3)))(cache_row_update_byte)
+ <= cache_row_update_value;
+ end if;
+ if cache_row_update_hi='1' then
+ report "DISPATCH: Updating block data via write: $" & to_hstring((cache_row_update_address&"001")+cache_row_update_byte)
+ & " gets $" & to_hstring(cache_row_update_value_hi);
+ block_data(to_integer(cache_row_update_address(4 downto 3)))(cache_row_update_byte+1)
+ <= cache_row_update_value_hi;
+ end if;
+ show_block := true;
+ end if;
+ end if;
+
+ if invalidate_read_cache='1' then
+ report "CACHE: Invalidating read cache due to write congestion.";
+ cache_row0_valids <= (others => '0');
+ cache_row1_valids <= (others => '0');
+ block_valid <= '0';
+ current_cache_line_valid_drive <= '0';
+ last_cache_row_update_toggle <= cache_row_update_toggle;
+ end if;
+
+ case state is
+ when StartupDelay =>
+ null;
+ when ReadAbort =>
+ -- Make sure we don't abort a read so quickly, that we allow
+ -- glitching of clock line with clock phase shifting
+ hr_cs0 <= '1';
+ state <= Idle;
+ when Idle =>
+ report "Tristating hr_d";
+ hr_d <= (others => 'Z');
+
+ read_request_held <= '0';
+ write_request_held <= '0';
+
+ if not cache_enabled then
+ busy_internal <= '0';
+ end if;
+
+ first_transaction <= '0';
+ is_block_read <= false;
+ is_prefetch <= ram_prefetch;
+ is_expected_to_respond <= ram_normalfetch;
+ is_vic_fetch <= false;
+
+ -- All commands need the clock offset by 1/2 cycle
+ hr_clk_phaseshift <= write_phase_shift;
+ hr_clk_fast <= '1';
+
+ pause_phase <= '0';
+ countdown_timeout <= '0';
+
+ -- Clear write buffer flags when they are empty
+ if write_collect0_dispatchable = '0' then
+ if write_collect0_toolate = '1' then
+ show_collect0 := true;
+ end if;
+ write_collect0_toolate <= '0';
+ write_collect0_flushed <= '0';
+ end if;
+ if write_collect1_dispatchable = '0' then
+ if write_collect1_toolate = '1' then
+ show_collect1 := true;
+ end if;
+ write_collect1_toolate <= '0';
+ write_collect1_flushed <= '0';
+ end if;
+
+ -- Mark us ready for a new job, or pick up a new job
+ report
+ "r_t=" & std_logic'image(request_toggle)
+ & ", l_r_t=" & std_logic'image(last_request_toggle)
+ & ", hr_clk=" & std_logic'image(hr_clk)
+ & ", rwr_counter = " & integer'image(to_integer(rwr_counter));
+
+ if rwr_counter /= to_unsigned(0,8) then
+ rwr_counter <= rwr_counter - 1;
+ hr_d <= x"bb";
+ end if;
+ if rwr_counter = to_unsigned(1,8) then
+ rwr_waiting <= '0';
+ end if;
+
+ -- Phase 101 guarantees that the clock base change will happen
+ -- within the comming clock cycle
+ if rwr_waiting='0' and hr_clock_phase165 = "10" then
+ if (viciv_request_toggle /= viciv_last_request_toggle)
+ -- Only start VIC-IV fetches if we don't have a transaction
+ -- already waiting to go.
+ and ((request_toggle = last_request_toggle) or viciv_debug_priority='1')
+ then
+ report "VIC: Received data request for $" & to_hstring(viciv_addr&"000")
+ & ", bank = $" & to_hstring(viciv_bank&"0000000000000000000");
+ -- VIC-IV is asking for 8 bytes of data
+ viciv_last_request_toggle <= viciv_request_toggle;
+
+ viciv_request_count <= viciv_request_count + 1;
+
+ -- Prepare command vector
+ hr_command(47) <= '1'; -- READ
+ hr_command(46) <= '0'; -- Memory, not register space
+ hr_command(45) <= '1'; -- linear
+ hr_command(44 downto 32) <= (others => '0'); -- unused upper address bits
+ hr_command(15 downto 3) <= (others => '0'); -- reserved bits
+ hr_command(34 downto 31) <= viciv_bank(3 downto 0);
+ hr_command(30 downto 16) <= viciv_addr(18 downto 4);
+ hr_command(2) <= viciv_addr(3);
+ hr_command(1 downto 0) <= "00";
+
+ -- We want
+ hyperram0_select <= not viciv_bank(4);
+ hyperram1_select <= viciv_bank(4);
+
+ hyperram_access_address(26 downto 19) <= viciv_bank;
+ hyperram_access_address(18 downto 3) <= viciv_addr(18 downto 3);
+ hyperram_access_address(2 downto 0) <= (others => '0');
+
+ busy_internal <= '1';
+ ram_reading_held <= '1';
+ is_expected_to_respond <= false;
+ is_vic_fetch <= true;
+ countdown <= 6;
+ countdown_is_zero <= '0';
+ config_reg_write <= '0';
+ hr_reset <= '1'; -- active low reset
+ pause_phase <= '0';
+
+ if fast_cmd_mode='1' then
+ state <= HyperRAMOutputCommand;
+ hr_clk_fast <= '1';
+ hr_clk_phaseshift <= write_phase_shift;
+ else
+ state <= HyperRAMOutputCommandSlow;
+ hr_clk_fast <= '0';
+ hr_clk_phaseshift <= write_phase_shift;
+ end if;
+ elsif prefetch_when_idle then
+ prefetch_when_idle <= false;
+ report "DISPATCHER: Dispatching chained pre-fetch";
+ tempaddr(26 downto 5) := current_cache_line_address(26 downto 5) + 1;
+ tempaddr(4 downto 0) := "00000";
+ hyperram_access_address <= tempaddr;
+
+ -- We are reading on a 32 byte boundary, so command formation is
+ -- simpler.
+ hr_command(47) <= '1'; -- Read
+ hr_command(45) <= '1'; -- Linear read, not wrapped
+ hr_command(34 downto 17) <= tempaddr(22 downto 5);
+ hr_command(16 downto 0) <= (others => '0');
+
+ hyperram0_select <= not tempaddr(23);
+ hyperram1_select <= tempaddr(23);
+ hr_reset <= '1';
+ pause_phase <= '0';
+ is_prefetch <= true;
+ ram_reading_held <= '1';
+ is_expected_to_respond <= false;
+
+ if fast_cmd_mode='1' then
+ state <= HyperRAMOutputCommand;
+ hr_clk_fast <= '1';
+ hr_clk_phaseshift <= write_phase_shift;
+ else
+ state <= HyperRAMOutputCommandSlow;
+ hr_clk_fast <= '0';
+ hr_clk_phaseshift <= write_phase_shift;
+ end if;
+
+ countdown <= 6;
+ config_reg_write <= '0';
+ countdown_is_zero <= '0';
+
+ report "DISPATCH: Dispatching pre-fetch of $" & to_hstring(tempaddr)
+ & " in response to giving last row to current_cache_line";
+ -- Mark a cache line to receive the pre-fetched data, so that we don't
+ -- have to wait for it all to turn up, before being able to return
+ -- the first 8 bytes
+ mark_cache_for_prefetch <= '1';
+
+ elsif (request_toggle /= last_request_toggle)
+ -- Only commence reads AFTER all pending writes have flushed,
+ -- to ensure cache coherence (there are corner-cases here with
+ -- chained writes, block reads and other bits and pieces).
+ and write_collect0_dispatchable='0'
+ and write_collect1_dispatchable='0' then
+ report "WAITING for job";
+ ram_reading_held <= ram_reading;
+
+ if ram_reading = '1' then
+ report "Waiting to start read";
+ request_accepted <= request_toggle;
+ last_request_toggle <= request_toggle;
+ state <= ReadSetup;
+ report "Accepting job";
+ busy_internal <= '1';
+ else
+ report "Waiting to start write";
+ report "Setting state to WriteSetup. random_bits=" & to_hstring(random_bits);
+ request_accepted <= request_toggle;
+ last_request_toggle <= request_toggle;
+ state <= WriteSetup;
+ report "Accepting job";
+ busy_internal <= '1';
+
+ end if;
+ elsif (write_collect0_dispatchable = '1')
+ -- But only if the other collector doesn't have an address that
+ -- would chain to us.
+-- and ((write_collect0_address /= (write_collect1_address + 1)) or write_collect1_dispatchable='0')
+ -- XXX The following slows access down noticeably through
+ -- inefficient scheduling.
+ and ((write_collect0_address_matches_write_collect1_address_plus_1='1') or write_collect1_dispatchable='0')
+ then
+ -- Do background write.
+ busy_internal <= '0';
+ request_accepted <= request_toggle;
+ is_prefetch <= false;
+ is_expected_to_respond <= false;
+
+ report "DISPATCH: Writing out collect0 @ $" & to_hstring(write_collect0_address&"000");
+
+ -- Mark the write buffer as being processed.
+ write_collect0_flushed <= '0';
+ -- And that it is not (yet) too late to add extra bytes to the write.
+ write_collect0_toolate <= '0';
+
+ background_write_next_address <= write_collect0_address;
+ background_write_next_address_matches_collect0 <= '1';
+ background_write <= '1';
+ background_write_fetch <= '1';
+ background_write_source <= '0'; -- collect 0
+ report "background_write_source = 0";
+
+ config_reg_write <= write_collect0_address(25);
+
+ -- Prepare command vector
+ hr_command(47) <= '0'; -- WRITE
+ hr_command(46) <= write_collect0_address(25); -- Memory, not register space
+ hr_command(45) <= '1'; -- linear
+ hr_command(44 downto 35) <= (others => '0'); -- unused upper address bits
+ hr_command(15 downto 3) <= (others => '0'); -- reserved bits
+ hr_command(34 downto 16) <= write_collect0_address(22 downto 4);
+ hr_command(2) <= write_collect0_address(3);
+ hr_command(1 downto 0) <= "00";
+ hr_reset <= '1'; -- active low reset
+
+ hyperram0_select <= not write_collect0_address(23);
+ hyperram1_select <= write_collect0_address(23);
+
+ hyperram_access_address(26 downto 3) <= write_collect0_address;
+ hyperram_access_address(2 downto 0) <= (others => '0');
+
+ ram_reading_held <= '0';
+
+ -- This is the delay before we assert CS
+
+ -- We have to use this intermediate stage to get the clock
+ -- phase right.
+ state <= StartBackgroundWrite;
+
+ if write_collect0_address(25)='1' then
+ -- 48 bits of CA followed by 16 bit register value
+ -- (we shift the buffered config register values out automatically)
+ countdown <= 6 + 1;
+ else
+ countdown <= 6;
+ end if;
+ countdown_is_zero <= '0';
+
+ elsif write_collect1_dispatchable = '1' then
+ busy_internal <= '0';
+ request_accepted <= request_toggle;
+
+ is_prefetch <= false;
+ is_expected_to_respond <= false;
+
+ report "DISPATCH: Writing out collect1 @ $" & to_hstring(write_collect1_address&"000");
+
+ -- Mark the write buffer as being processed.
+ write_collect1_flushed <= '0';
+ -- And that it is not (yet) too late to add extra bytes to the write.
+ write_collect1_toolate <= '0';
+ show_collect1 := true;
+
+ config_reg_write <= write_collect1_address(25);
+
+ background_write_next_address <= write_collect1_address;
+ background_write_next_address_matches_collect1 <= '1';
+ background_write <= '1';
+ background_write_fetch <= '1';
+ background_write_source <= '1'; -- collect 1
+ report "background_write_source = 1";
+
+ -- Prepare command vector
+ hr_command(47) <= '0'; -- WRITE
+ hr_command(46) <= write_collect1_address(25); -- Memory, not register space
+ hr_command(45) <= '1'; -- linear
+ hr_command(44 downto 35) <= (others => '0'); -- unused upper address bits
+ hr_command(15 downto 3) <= (others => '0'); -- reserved bits
+ hr_command(34 downto 16) <= write_collect1_address(22 downto 4);
+ hr_command(2) <= write_collect1_address(3);
+ hr_command(1 downto 0) <= "00";
+
+ ram_reading_held <= '0';
+
+ hyperram0_select <= not write_collect1_address(23);
+ hyperram1_select <= write_collect1_address(23);
+
+ hyperram_access_address(26 downto 3) <= write_collect1_address;
+ hyperram_access_address(2 downto 0) <= (others => '0');
+
+ hr_reset <= '1'; -- active low reset
+
+ state <= StartBackgroundWrite;
+
+ if write_collect1_address(25)='1' then
+ -- 48 bits of CA followed by 16 bit register value
+ -- (we shift the buffered config register values out automatically)
+ countdown <= 6 + 1;
+ else
+ countdown <= 6;
+ end if;
+ countdown_is_zero <= '0';
+
+ report "clk_queue <= '00'";
+
+ else
+ report "Clearing busy_internal";
+ busy_internal <= '0';
+ request_accepted <= request_toggle;
+ end IF;
+ -- Release CS line between transactions
+ report "Releasing hyperram CS lines";
+ hr_cs0 <= '1';
+ end if;
+
+ when StartBackgroundWrite =>
+ report "in StartBackgroundWrite to synchronise with clock";
+ pause_phase <= '0';
+ if fast_cmd_mode='1' then
+ state <= HyperRAMOutputCommand;
+ hr_clk_phaseshift <= write_phase_shift;
+ hr_clk_fast <= '1';
+ else
+ state <= HyperRAMOutputCommandSlow;
+ hr_clk_phaseshift <= write_phase_shift;
+ hr_clk_fast <= '0';
+ end if;
+
+ when ReadSetup =>
+ report "Setting up to read $" & to_hstring(ram_address) & " ( address = $" & to_hstring(address) & ")";
+
+ -- Prepare command vector
+ hr_command(47) <= '1'; -- READ
+ -- Map actual RAM to bottom 32MB of 64MB space (repeated 4x)
+ -- and registers to upper 32MB
+-- hr_command(46) <= '1'; -- Memory address space (1) / Register
+ hr_command(46) <= ram_address(25); -- Memory address space (1) / Register
+ -- address space select (0) ?
+ hr_command(45) <= '1'; -- Linear access (not wrapped)
+ hr_command(44 downto 37) <= (others => '0'); -- unused upper address bits
+ hr_command(34 downto 16) <= ram_address(22 downto 4);
+ hr_command(15 downto 3) <= (others => '0'); -- reserved bits
+ if ram_address(25) = '0' then
+ -- Always read on 8 byte boundaries, and read a full cache line
+ hr_command(2) <= ram_address(3);
+ hr_command(1 downto 0) <= "00";
+ else
+ -- Except that register reads are weird: They read the same 2 bytes
+ -- over and over again, so we have to make it set bit 0 of the CA
+ -- for the "odd" registers"
+ hr_command(2 downto 1) <= "00";
+ hr_command(0) <= ram_address(3);
+ end if;
+
+ hyperram0_select <= not ram_address(23);
+ hyperram1_select <= ram_address(23);
+
+ hyperram_access_address <= ram_address;
+
+ hr_reset <= '1'; -- active low reset
+ pause_phase <= '0';
+
+ if fast_cmd_mode='1' then
+ state <= HyperRAMOutputCommand;
+ hr_clk_fast <= '1';
+ hr_clk_phaseshift <= write_phase_shift;
+ else
+ state <= HyperRAMOutputCommandSlow;
+ hr_clk_fast <= '0';
+ hr_clk_phaseshift <= write_phase_shift;
+ end if;
+
+ countdown <= 6;
+ config_reg_write <= '0';
+ countdown_is_zero <= '0';
+
+ when WriteSetup =>
+
+ report "Preparing hr_command etc for write to $" & to_hstring(ram_address);
+
+ if not cache_enabled then
+ background_write_count <= 2;
+ background_write <= '0';
+ end if;
+
+ config_reg_write <= ram_address(25);
+
+ -- Prepare command vector
+ -- As HyperRAM addresses on 16bit boundaries, we shift the address
+ -- down one bit.
+ hr_command(47) <= '0'; -- WRITE
+ hr_command(46) <= ram_address(25); -- Memory, not register space
+ hr_command(45) <= '1'; -- linear
+
+ hr_command(44 downto 35) <= (others => '0'); -- unused upper address bits
+ hr_command(15 downto 3) <= (others => '0'); -- reserved bits
+
+ hr_command(34 downto 16) <= ram_address(22 downto 4);
+ hr_command(2 downto 0) <= ram_address(3 downto 1);
+
+ hr_reset <= '1'; -- active low reset
+
+ hyperram0_select <= not ram_address(23);
+ hyperram1_select <= ram_address(23);
+
+ hyperram_access_address <= ram_address;
+
+ pause_phase <= '0';
+
+ if start_delay_expired = '1' then
+ if fast_cmd_mode='1' then
+ state <= HyperRAMOutputCommand;
+ hr_clk_fast <= '1';
+ hr_clk_phaseshift <= write_phase_shift;
+ else
+ state <= HyperRAMOutputCommandSlow;
+ hr_clk_fast <= '0';
+ hr_clk_phaseshift <= write_phase_shift;
+ end if;
+ end if;
+ if ram_address(25)='1' then
+ -- 48 bits of CA followed by 16 bit register value
+ -- (we shift the buffered config register values out automatically)
+ countdown <= 6 + 1;
+ else
+ countdown <= 6;
+ end if;
+ countdown_is_zero <= '0';
+
+ when HyperRAMOutputCommandSlow =>
+ report "Writing command, hyperram_access_address=$" & to_hstring(hyperram_access_address);
+ report "hr_command = $" & to_hstring(hr_command);
+ -- Call HyperRAM to attention
+ hr_cs0 <= not hyperram0_select;
+
+ hr_rwds <= 'Z';
+
+ -- Prepare for reading block data
+ is_block_read <= false;
+ if (hyperram_access_address(4 downto 3) = "00") and block_read_enable='1' and (ram_reading_held='1')
+ and (is_vic_fetch = false) then
+ block_valid <= '0';
+ block_address <= hyperram_access_address(26 downto 5);
+ is_block_read <= true;
+ end if;
+
+ pause_phase <= not pause_phase;
+
+ if pause_phase='1' then
+ hr_clk_phaseshift <= write_phase_shift;
+
+ if countdown_timeout='1' then
+ -- Finished shifting out
+ if ram_reading_held = '1' then
+ -- Reading: We can just wait until hr_rwds has gone low, and then
+ -- goes high again to indicate the first data byte
+ countdown <= 63;
+ countdown_is_zero <= '0';
+ hr_rwds_high_seen <= '0';
+ countdown_timeout <= '0';
+ if fast_read_mode='1' then
+ hr_clk_fast <= '1';
+ state <= HyperRAMReadWait;
+ else
+ pause_phase <= '1';
+ hr_clk_fast <= '0';
+ state <= HyperRAMReadWaitSlow;
+ end if;
+ elsif config_reg_write='1' and ram_reading_held='0' then
+ -- Config register write.
+ -- These are a bit weird, as they have no latency, and all 16
+ -- bits have to get written at once. So we will have 2 buffer
+ -- registers that get setup, and then ANY write to the register
+ -- area will write those values, which we have done by shifting
+ -- those through and sending 48+16 bits instead of the usual
+ -- 48.
+ if background_write='1' then
+ if background_write_source = '0' then
+ write_collect0_flushed <= '1';
+ show_collect0 := true;
+ else
+ write_collect1_flushed <= '1';
+ show_collect1 := true;
+ end if;
+ end if;
+
+ report "Finished writing config register";
+ state <= HyperRAMFinishWriting;
+ else
+ -- Writing to memory, so count down the correct number of cycles;
+ -- Initial latency is reduced by 2 cycles for the last bytes
+ -- of the access command, and by 1 more to cover state
+ -- machine latency
+ if hyperram1_select='0' then
+ countdown <= to_integer(write_latency);
+ else
+ countdown <= to_integer(write_latency2);
+ end if;
+ -- XXX Doesn't work if write_latency(2) is $00
+ countdown_is_zero <= '0';
+
+
+ -- We are not just about ready to start writing, so mark the
+ -- write buffer as too late to be added to, because we will
+ -- snap-shot it in a moment.
+ if background_write = '1' then
+ report "WRITE: Asserting toolate signal for collect" & std_logic'image(background_write_source);
+ background_write_count <= 4 + 2;
+ -- We know we can do upto 128 bytes at least per write,
+ -- before a refresh is required. So allow 16x8 byte writes to
+ -- be chained.
+ write_continues <= write_continues_max;
+ if background_write_source = '0' then
+ write_collect0_toolate <= '1';
+ write_collect0_flushed <= '0';
+ show_collect0 := true;
+ else
+ write_collect1_toolate <= '1';
+ write_collect1_flushed <= '0';
+ show_collect1 := true;
+ end if;
+ end if;
+ countdown_timeout <= '0';
+ if fast_write_mode='1' then
+ hr_clk_fast <= '1';
+ state <= HyperRAMDoWrite;
+ else
+ hr_clk_fast <= '0';
+ state <= HyperRAMDoWriteSlow;
+ end if;
+ end if;
+ end if;
+
+ else
+
+ -- Toggle data while clock steady
+ report "Presenting hr_command byte on hr_d = $" & to_hstring(hr_command(47 downto 40))
+ & ", clock = " & std_logic'image(hr_clk)
+ & ", countdown = " & integer'image(countdown);
+
+ hr_d <= hr_command(47 downto 40);
+ hr_command(47 downto 8) <= hr_command(39 downto 0);
+
+ -- Also shift out config register values, if required
+ if config_reg_write='1' and ram_reading_held='0' then
+ report "shifting in conf value $" & to_hstring(conf_buf0);
+ hr_command(7 downto 0) <= conf_buf0;
+ conf_buf0 <= conf_buf1;
+ conf_buf1 <= conf_buf0;
+ else
+ hr_command(7 downto 0) <= x"00";
+ end if;
+
+ report "Writing command byte $" & to_hstring(hr_command(47 downto 40));
+
+ if countdown = 3 and config_reg_write='1' then
+ if background_write='1' then
+ if background_write_source = '0' then
+ write_collect0_toolate <= '1';
+ else
+ write_collect1_toolate <= '1';
+ end if;
+ end if;
+ end if;
+
+ if countdown = 3 and (config_reg_write='0' or ram_reading_held='1') then
+ extra_latency <= hr_rwds;
+ if (hr_rwds='1' and hyperram0_select='1') then
+ report "Applying extra latency";
+ end if;
+ end if;
+ if countdown = 1 then
+ countdown_is_zero <= '1';
+ end if;
+ if countdown /= 0 then
+ countdown <= countdown - 1;
+ else
+ report "asserting countdown_timeout";
+ countdown_timeout <= '1';
+ end if;
+ end if;
+ byte_phase <= to_unsigned(0,6);
+ write_byte_phase <= '0';
+ when HyperRAMOutputCommand =>
+ report "Writing command";
+ -- Call HyperRAM to attention
+ hr_cs0 <= not hyperram0_select;
+
+ hr_rwds <= 'Z';
+
+ hr_clk_phaseshift <= write_byte_phase;
+
+ -- Toggle data while clock steady
+-- report "Presenting hr_command byte on hr_d = $" & to_hstring(hr_command(47 downto 40))
+-- & ", clock = " & std_logic'image(hr_clock)
+-- & ", next_is_data = " & std_logic'image(next_is_data)
+-- & ", countdown = " & integer'image(countdown)
+-- & ", cs0= " & std_logic'image(hr_cs0);
+
+ hr_d <= hr_command(47 downto 40);
+ hr_command(47 downto 8) <= hr_command(39 downto 0);
+
+ -- Also shift out config register values, if required
+ if config_reg_write='1' and ram_reading_held='0' then
+ report "shifting in conf value $" & to_hstring(conf_buf0);
+ hr_command(7 downto 0) <= conf_buf0;
+ conf_buf0 <= conf_buf1;
+ conf_buf1 <= conf_buf0;
+ else
+ hr_command(7 downto 0) <= x"00";
+ end if;
+
+ report "Writing command byte $" & to_hstring(hr_command(47 downto 40));
+
+ if countdown = 3 and (config_reg_write='0' or ram_reading_held='1') then
+ if hyperram0_select='1' then
+ extra_latency <= hr_rwds;
+ end if;
+ if (hr_rwds='1' and hyperram0_select='1') then
+ report "Applying extra latency";
+ end if;
+ end if;
+ if countdown = 1 then
+ countdown_is_zero <= '1';
+ else
+ countdown_is_zero <= '0';
+ end if;
+ if countdown_is_zero = '1' then
+ -- Finished shifting out
+ if ram_reading_held = '1' then
+ -- Reading: We can just wait until hr_rwds has gone low, and then
+ -- goes high again to indicate the first data byte
+ countdown <= 63;
+ countdown_is_zero <= '0';
+ hr_rwds_high_seen <= '0';
+ if fast_read_mode='1' then
+ hr_clk_fast <= '1';
+ state <= HyperRAMReadWait;
+ else
+ pause_phase <= '0';
+ hr_clk_fast <= '0';
+ state <= HyperRAMReadWaitSlow;
+ end if;
+ elsif config_reg_write='1' and ram_reading_held='0' then
+ -- Config register write.
+ -- These are a bit weird, as they have no latency, and all 16
+ -- bits have to get written at once. So we will have 2 buffer
+ -- registers that get setup, and then ANY write to the register
+ -- area will write those values, which we have done by shifting
+ -- those through and sending 48+16 bits instead of the usual
+ -- 48.
+
+ if background_write='1' then
+ if background_write_source = '0' then
+ write_collect0_flushed <= '1';
+ show_collect0 := true;
+ else
+ write_collect1_flushed <= '1';
+ show_collect1 := true;
+ end if;
+ end if;
+
+ report "Finished writing config register";
+ state <= HyperRAMFinishWriting;
+ else
+ -- Writing to memory, so count down the correct number of cycles;
+ -- Initial latency is reduced by 2 cycles for the last bytes
+ -- of the access command, and by 1 more to cover state
+ -- machine latency
+ if hyperram0_select='1' then
+ countdown <= to_integer(write_latency);
+ else
+ countdown <= to_integer(write_latency2);
+ end if;
+ -- XXX Assumes write_latency > 0
+ countdown_is_zero <= '0';
+
+ -- We are now just about ready to start writing, so mark the
+ -- write buffer as too late to be added to, because we will
+ -- snap-shot it in a moment.
+ if background_write = '1' then
+ report "WRITE: Asserting toolate signal";
+ background_write_count <= 4 + 2;
+ -- We know we can do upto 128 bytes at least per write,
+ -- before a refresh is required. So allow 16x8 byte writes to
+ -- be chained.
+ write_continues <= write_continues_max;
+ if background_write_source = '0' then
+ write_collect0_toolate <= '1';
+ write_collect0_flushed <= '0';
+ show_collect0 := true;
+ else
+ write_collect1_toolate <= '1';
+ write_collect1_flushed <= '0';
+ show_collect1 := true;
+ end if;
+ end if;
+
+ countdown_timeout <= '0';
+ if fast_write_mode='1' then
+ state <= HyperRAMDoWrite;
+ else
+ state <= HyperRAMDoWriteSlow;
+ end if;
+ end if;
+ end if;
+ byte_phase <= to_unsigned(0,6);
+ write_byte_phase <= '0';
+
+ when HyperRAMDoWrite =>
+ hr_clk_phaseshift <= write_phase_shift;
+
+ report "WRITE: LatencyWait state, bg_wr=" & std_logic'image(background_write)
+ & ", count=" & integer'image(background_write_count);
+
+ -- Now snap-shot the write buffer data, and mark the slot as flushed
+ if background_write = '1' then
+ if background_write_source = '0' and write_collect0_toolate='1' and write_collect0_flushed = '0' then
+ write_collect0_flushed <= '1';
+ show_collect0 := true;
+ report "WRITE: background_write_data copied from write_collect0. Valids = " & to_string(write_collect0_valids);
+ background_write_fetch <= '1';
+ background_write_data <= write_collect0_data;
+ background_write_valids <= write_collect0_valids;
+
+ elsif background_write_source = '1' and write_collect1_toolate='1' and write_collect1_flushed = '0' then
+ write_collect1_flushed <= '1';
+ show_collect1 := true;
+ report "WRITE: background_write_data copied from write_collect1";
+ background_write_data <= write_collect1_data;
+ background_write_valids <= write_collect1_valids;
+ end if;
+
+ -- Invalidate read cache when writing
+ if cache_enabled and hyperram_access_address_matches_cache_row0 = '1' then
+ report "Zeroing cache_row0_valids";
+ cache_row0_valids <= (others => '0');
+ elsif cache_enabled and hyperram_access_address_matches_cache_row1 = '1' then
+ report "Zeroing cache_row1_valids";
+ cache_row1_valids <= (others => '0');
+ end if;
+ end if;
+
+ report "latency countdown = " & integer'image(countdown);
+
+ -- Begin write mask pre-amble
+ if ram_reading_held = '0' and countdown = 2 then
+ hr_rwds <= '0';
+ hr_d <= x"BE"; -- "before" data byte
+ end if;
+
+ if countdown /= 0 then
+ countdown <= countdown - 1;
+ end if;
+ if countdown = 1 then
+ countdown_is_zero <= '1';
+ end if;
+ if countdown_is_zero='1' then
+ if extra_latency='1' then
+ report "Waiting 6 more cycles for extra latency";
+ -- If we were asked to wait for extra latency,
+ -- then wait another 6 cycles.
+ extra_latency <= '0';
+
+ if hyperram0_select='1' then
+ countdown <= to_integer(extra_write_latency);
+ else
+ countdown <= to_integer(extra_write_latency2);
+ end if;
+ -- Assumes extra latency is not zero
+ countdown_is_zero <= '0';
+ else
+ -- Latency countdown for writing is over, we can now
+ -- begin writing bytes.
+
+ -- HyperRAM works on 16-bit fundamental transfers.
+ -- This means we need to have two half-cycles, and pick which
+ -- one we want to write during.
+ -- If RWDS is asserted, then the write is masked, i.e., won't
+ -- occur.
+ -- In this first
+
+ report "Presenting hr_d with ram_wdata or background data";
+ if background_write='1' then
+ report "WRITE: Writing background byte $" & to_hstring(background_write_data(0))
+ & ", valids= " & to_string(background_write_valids)
+ & ", background words left = " & integer'image(background_write_count);
+ hr_d <= background_write_data(0);
+ background_write_data(0) <= background_write_data(1);
+ background_write_data(1) <= background_write_data(2);
+ background_write_data(2) <= background_write_data(3);
+ background_write_data(3) <= background_write_data(4);
+ background_write_data(4) <= background_write_data(5);
+ background_write_data(5) <= background_write_data(6);
+ background_write_data(6) <= background_write_data(7);
+ background_write_data(7) <= x"00";
+
+ hr_rwds <= not background_write_valids(0);
+ background_write_valids(0 to 6) <= background_write_valids(1 to 7);
+ background_write_valids(7) <= '0';
+
+ if background_write_count = 0 and background_chained_write = '1' then
+ background_write_fetch <= '1';
+ end if;
+
+ else
+ -- XXX Doesn't handle 16-bit writes properly. But that's
+ -- okay, as they are only supported with the cache and
+ -- write-collecting, anyway.
+ hr_d <= ram_wdata;
+ hr_rwds <= hyperram_access_address(0) xor write_byte_phase;
+ end if;
+
+ -- Write byte
+ write_byte_phase <= '1';
+ if background_write='0' then
+ if write_byte_phase = '0' and hyperram_access_address(0)='1' then
+ report "Masking even byte";
+ hr_d <= x"ee"; -- even "masked" data byte
+ elsif write_byte_phase = '1' and hyperram_access_address(0)='0' then
+ report "Masking odd byte";
+ hr_d <= x"0d"; -- odd "masked" data byte
+ end if;
+ if background_write_count /= 0 then
+ background_write_count <= background_write_count - 1;
+ else
+ state <= HyperRAMFinishWriting;
+ end if;
+ elsif write_byte_phase='1' then
+ report "WRITE: Decrementing background_write_count from " & integer'image(background_write_count);
+ if background_write_count /= 0 then
+ background_write_count <= background_write_count - 1;
+ else
+ report "Advancing to HyperRAMFinishWriting";
+ hr_clk_phaseshift <= write_phase_shift;
+ end if;
+ end if;
+ end if;
+ end if;
+ when HyperRAMDoWriteSlow =>
+ pause_phase <= not pause_phase;
+
+ -- Update cache
+ if cache_row0_address_matches_ram_address = '1' then
+ if ram_wdata_enlo_drive='1' then
+ cache_row0_valids(to_integer(ram_address_drive(2 downto 0))) <= '1';
+ cache_row0_data(to_integer(ram_address_drive(2 downto 0))) <= ram_wdata_drive;
+ end if;
+ if ram_wdata_enhi_drive='1' then
+ cache_row0_valids(to_integer(ram_address_drive(2 downto 0))+1) <= '1';
+ cache_row0_data(to_integer(ram_address_drive(2 downto 0))+1) <= ram_wdata_hi_drive;
+ end if;
+ show_cache0 := true;
+ elsif cache_row1_address_matches_ram_address='1' then
+ if ram_wdata_enlo_drive='1' then
+ cache_row1_valids(to_integer(ram_address_drive(2 downto 0))) <= '1';
+ cache_row1_data(to_integer(ram_address_drive(2 downto 0))) <= ram_wdata_drive;
+ end if;
+ if ram_wdata_enhi_drive='1' then
+ cache_row1_valids(to_integer(ram_address_drive(2 downto 0))+1) <= '1';
+ cache_row1_data(to_integer(ram_address_drive(2 downto 0))+1) <= ram_wdata_hi_drive;
+ end if;
+ show_cache1 := true;
+ else
+ if random_bits(1)='0' then
+ report "Zeroing cache_row0_valids";
+ cache_row0_valids <= (others => '0');
+ cache_row0_address <= ram_address_drive(26 downto 3);
+ cache_row0_address_matches_ram_address <= '1';
+ if ram_wdata_enlo_drive='1' then
+ cache_row0_valids(to_integer(ram_address_drive(2 downto 0))) <= '1';
+ cache_row0_data(to_integer(ram_address_drive(2 downto 0))) <= ram_wdata_drive;
+ end if;
+ if ram_wdata_enhi_drive='1' then
+ cache_row0_valids(to_integer(ram_address_drive(2 downto 0))+1) <= '1';
+ cache_row0_data(to_integer(ram_address_drive(2 downto 0))+1) <= ram_wdata_hi_drive;
+ end if;
+ show_cache0 := true;
+ else
+ report "Zeroing cache_row1_valids";
+ cache_row1_valids <= (others => '0');
+ cache_row1_address <= ram_address_drive(26 downto 3);
+ cache_row1_address_matches_ram_address <= '1';
+ if ram_wdata_enlo_drive='1' then
+ cache_row1_valids(to_integer(ram_address_drive(2 downto 0))) <= '1';
+ cache_row1_data(to_integer(ram_address_drive(2 downto 0))) <= ram_wdata_drive;
+ end if;
+ if ram_wdata_enhi_drive='1' then
+ cache_row1_valids(to_integer(ram_address_drive(2 downto 0))+1) <= '1';
+ cache_row1_data(to_integer(ram_address_drive(2 downto 0))+1) <= ram_wdata_hi_drive;
+ end if;
+ show_cache1 := true;
+ end if;
+ end if;
+
+ -- Fetch takes 2 cycles, so schedule one cycle before last read
+ -- and shift, so that it happens after that last shift, but
+ -- before it is needed again.
+ if background_write_count = 0 and pause_phase = '0' then
+ -- See if we have another write collect that we can
+ -- continue with
+ -- XXX We suspect that chained writes might be problematic on the
+ -- external hyperram for some strange reason, so disable them.
+ if write_continues /= 0 and background_chained_write='1' then
+ if background_write_fetch = '0' then
+ report "WRITECONTINUE: Continuing write: Requesting fetch.";
+ background_write_fetch <= '1';
+ end if;
+ else
+ report "WRITECONTINUE: No continuation. Terminating write.";
+ report "asserting countdown_timeout";
+ countdown_timeout <= '1';
+ end if;
+ end if;
+
+ report "WRITE: LatencyWait state, bg_wr=" & std_logic'image(background_write)
+ & ", count=" & integer'image(background_write_count)
+ & ", background_write_fetch = " & std_logic'image(background_write_fetch)
+ & ", background_write_valids = " & to_string(background_write_valids)
+ & ", write_blocked=" & std_logic'image(write_blocked);
+
+ -- Now snap-shot the write buffer data, and mark the slot as flushed
+ if background_write = '1' and
+ ( (background_write_next_address_matches_collect0 = '1')
+ or (background_write_next_address_matches_collect1 = '1') )
+ then
+ if background_chained_write = '0' then
+ report "WRITE: background_chained_write <= 1";
+ end if;
+ background_chained_write <= '1';
+ else
+ if background_chained_write = '1' then
+ report "WRITE: background_chained_write <= 0";
+ end if;
+ background_chained_write <= '0';
+
+ if hr_clock_phase165="11" and (background_write_valids = "00000000")
+ and (read_request='1' or write_request='1' or write_blocked='1') then
+ report "LatencyWait: Aborting tail of background write due to incoming job/write_blocked";
+ state <= HyperRAMFinishWriting;
+ end if;
+
+
+ end if;
+
+ if background_write_fetch = '1' then
+ report "WRITE: Doing fetch of background write data";
+ background_write_fetch <= '0';
+ background_write_next_address <= background_write_next_address + 1;
+ write_continues <= write_continues - 1;
+ background_write_count <= 7;
+ if background_write_next_address_matches_collect0 = '1' and background_write_source = '0' then
+ show_collect0 := true;
+ report "WRITE: background_write_data copied from write_collect0 (@ $"
+ & to_hstring(write_collect0_address&"000")
+ & "). Valids = " & to_string(write_collect0_valids)
+ & ", next addr was $" & to_hstring(background_write_next_address&"000");
+
+ background_write_next_address <= write_collect0_address + 1;
+ background_write_next_address_matches_collect0 <= '0';
+ background_write_next_address_matches_collect1 <= collect1_matches_collect0_plus_1;
+
+ background_write_data <= write_collect0_data;
+ background_write_valids <= write_collect0_valids;
+ write_collect0_flushed <= '1';
+
+ elsif background_write_next_address_matches_collect1 = '1' and background_write_source = '1' then
+ show_collect1 := true;
+ report "WRITE: background_write_data copied from write_collect1. Valids = " & to_string(write_collect1_valids)
+ & ", next addr was $" & to_hstring(background_write_next_address&"000");
+ background_write_next_address <= write_collect1_address + 1;
+ background_write_next_address_matches_collect0 <= collect0_matches_collect1_plus_1;
+ background_write_next_address_matches_collect1 <= '0';
+
+ background_write_data <= write_collect1_data;
+ background_write_valids <= write_collect1_valids;
+ write_collect1_flushed <= '1';
+ else
+ report "WRITE: Write is not chained.";
+ background_chained_write <= '0';
+ end if;
+ end if;
+
+ if pause_phase = '1' then
+ hr_clk_phaseshift <= write_phase_shift;
+ if countdown_timeout = '1' then
+ report "Advancing to HyperRAMFinishWriting";
+ state <= HyperRAMFinishWriting;
+ end if;
+ else
+
+ report "latency countdown = " & integer'image(countdown);
+
+ -- Begin write mask pre-amble
+ if ram_reading_held = '0' and countdown = 2 then
+ hr_rwds <= '0';
+ hr_d <= x"BE"; -- "before" data byte
+ end if;
+
+ if countdown /= 0 then
+ countdown <= countdown - 1;
+ end if;
+ if countdown = 1 then
+ countdown_is_zero <= '1';
+ end if;
+ if countdown_is_zero = '1' then
+ if extra_latency='1' then
+ report "Waiting 6 more cycles for extra latency";
+ -- If we were asked to wait for extra latency,
+ -- then wait another 6 cycles.
+ extra_latency <= '0';
+ if hyperram0_select='1' then
+ countdown <= to_integer(extra_write_latency);
+ else
+ countdown <= to_integer(extra_write_latency2);
+ end if;
+ -- XXX Assumes extra_write_latency is not zero
+ countdown_is_zero <= '0';
+ else
+ -- Latency countdown for writing is over, we can now
+ -- begin writing bytes.
+
+ -- HyperRAM works on 16-bit fundamental transfers.
+ -- This means we need to have two half-cycles, and pick which
+ -- one we want to write during.
+ -- If RWDS is asserted, then the write is masked, i.e., won't
+ -- occur.
+ -- In this first
+
+ report "Presenting hr_d with ram_wdata or background data";
+ if background_write='1' then
+ report "WRITE: Writing background byte $" & to_hstring(background_write_data(0))
+ & ", valids= " & to_string(background_write_valids)
+ & ", background words left = " & integer'image(background_write_count);
+ hr_d <= background_write_data(0);
+
+ background_write_data(0) <= background_write_data(1);
+ background_write_data(1) <= background_write_data(2);
+ background_write_data(2) <= background_write_data(3);
+ background_write_data(3) <= background_write_data(4);
+ background_write_data(4) <= background_write_data(5);
+ background_write_data(5) <= background_write_data(6);
+ background_write_data(6) <= background_write_data(7);
+ background_write_data(7) <= x"00";
+
+ hr_rwds <= not background_write_valids(0);
+ background_write_valids(0 to 6) <= background_write_valids(1 to 7);
+ background_write_valids(7) <= '0';
+ else
+ -- XXX Doesn't handle 16-bit writes properly. But that's
+ -- okay, as they are only supported with the cache and
+ -- write-collecting, anyway.
+ hr_d <= ram_wdata;
+ hr_rwds <= hyperram_access_address(0) xor write_byte_phase;
+ end if;
+
+ -- Finish resetting write collectors when chaining
+ if write_collect0_dispatchable='0' and write_collect0_flushed='1' and write_collect0_toolate='1' then
+ report "WRITECONTINUE: Resetting collect0";
+ write_collect0_flushed <= '0';
+ write_collect0_toolate <= '0';
+ show_collect0 := true;
+ end if;
+ if write_collect1_dispatchable='0' and write_collect1_flushed='1' and write_collect1_toolate='1' then
+ report "WRITECONTINUE: Resetting collect1";
+ write_collect1_flushed <= '0';
+ write_collect1_toolate <= '0';
+ show_collect1 := true;
+ end if;
+
+ -- Write byte
+ write_byte_phase <= '1';
+ if background_write='0' then
+ if write_byte_phase = '0' and hyperram_access_address(0)='1' then
+ hr_d <= x"ee"; -- even "masked" data byte
+ elsif write_byte_phase = '1' and hyperram_access_address(0)='0' then
+ hr_d <= x"0d"; -- odd "masked" data byte
+ end if;
+ if background_write_count /= 0 then
+ background_write_count <= background_write_count - 1;
+ else
+ state <= HyperRAMFinishWriting;
+ end if;
+ else
+ report "WRITE: Decrementing background_write_count from " & integer'image(background_write_count)
+ & ", write_continues = " & integer'image(write_continues);
+ if background_write_count /= 0 then
+ background_write_count <= background_write_count - 1;
+ if background_write_count = 3 and write_continues /= 0 then
+ report "WRITECONTINUE: Checking for chained writes (" & integer'image(write_continues) & " more continues allowed)";
+ report "WRITECONTINUE: Am looking for $" & to_hstring(background_write_next_address&"000") &
+ ", options are 0:$" & to_hstring(write_collect0_address&"000") &
+ " and 1:$" & to_hstring(write_collect1_address&"000");
+ show_collect0 := true;
+ show_collect1 := true;
+ -- Get ready to commit next write block, if one is there
+ if write_continues /= 0 and write_collect0_toolate='0' and write_collect0_flushed = '0'
+ and background_write_next_address_matches_collect0='1' then
+ report "WRITECONTINUE: Marking collect0 @ $" & to_hstring(write_collect0_address&"000") & " for chained write.";
+ write_collect0_toolate <= '1';
+ background_write_source <= '0';
+ report "background_write_source = 0";
+ show_collect0 := true;
+ elsif write_continues /= 0 and write_collect1_toolate='0' and write_collect1_flushed = '0'
+ and background_write_next_address_matches_collect1='1' then
+ report "WRITECONTINUE: Marking collect1 @ $" & to_hstring(write_collect1_address&"000") & " for chained write.";
+ write_collect1_toolate <= '1';
+ background_write_source <= '1';
+ report "background_write_source = 1";
+ show_collect1 := true;
+ end if;
+ end if;
+ end if;
+
+ end if;
+ end if;
+ end if;
+ end if;
+ when HyperRAMFinishWriting =>
+ -- Mask writing from here on.
+ hr_cs0 <= '1';
+ hr_rwds <= 'Z';
+ hr_d <= x"FA"; -- "after" data byte
+ hr_clk_phaseshift <= write_phase_shift;
+ report "clk_queue <= '00'";
+ rwr_counter <= rwr_delay;
+ rwr_waiting <= '1';
+ report "returning to idle";
+ state <= Idle;
+ when HyperRAMReadWait =>
+ hr_rwds <= 'Z';
+ report "Presenting tri-state on hr_d";
+ hr_d <= (others => 'Z');
+ if countdown_is_zero = '0' then
+ countdown <= countdown - 1;
+ end if;
+ if countdown = 1 then
+ countdown_is_zero <= '1';
+ end if;
+ if countdown_is_zero = '1' then
+ -- Timed out waiting for read -- so return anyway, rather
+ -- than locking the machine hard forever.
+ rdata_hi <= x"DD";
+ rdata <= x"DD";
+ rdata(0) <= data_ready_toggle;
+ rdata(1) <= busy_internal;
+ report "asserting data_ready_strobe";
+ data_ready_strobe <= '1';
+ data_ready_strobe_hold <= '1';
+ rwr_counter <= rwr_delay;
+ rwr_waiting <= '1';
+ hr_clk_phaseshift <= write_phase_shift;
+ report "returning to idle";
+ state <= Idle;
+ end if;
+
+ -- Abort memory pre-fetching if we are asked to do something
+ if is_block_read and (not is_vic_fetch) then
+ if (read_request_prev='1' or write_request_prev='1') then
+ -- Okay, here is the tricky case: If the request is for data
+ -- that is in this block read, we DONT want to abort the read,
+ -- because starting a new request will almost always be slower.
+ report "DISPATCH: new request is for $" & to_hstring(address) & ", and we are reading $" & to_hstring(hyperram_access_address) & ", read = " & std_logic'image(read_request);
+ report "DISPATCH:"
+ & " read_request=" & std_logic'image(read_request)
+ & " read_request_held=" & std_logic'image(read_request_held)
+ & " address_matches_hyperram_access_address_block=" & std_logic'image(address_matches_hyperram_access_address_block);
+
+ if write_request_prev='1' and address_matches_hyperram_access_address_block='1' then
+ -- XXX We are writing to a block that we are pre-fetching.
+ -- The write will happen anyway. If we already have read the
+ -- byte, we can update it, else we have to abort the block, so
+ -- that the write can happen first.
+ if byte_phase_greater_than_address_low_bits = '0' then
+ report "DISPATCH: Aborting pre-fetch due to incoming conflicting write request";
+ state <= ReadAbort;
+ end if;
+
+ elsif read_request_prev='1' and address_matches_hyperram_access_address_block='1' then
+ -- New read request from later in this block.
+ -- We know that we will have the data soon.
+ -- The trick is coordinating our response.
+ -- If we have already read the byte, then it's relatively easy:
+ -- we can just return it, and pretend nothing happened...
+ -- except that the 80mhz state machine that talks to slow_devices
+ -- doesn't know that we can do this.
+ report "DISPATCH: Continuing with pre-fetch, because the read hits the block being read!";
+
+ -- Return the byte as soon as we have it available
+ -- We don't test request_toggle, as the outer 80MHz state
+ -- machine thinks we are still busy.
+ if address_matches_hyperram_access_address_block = '1' then
+ if byte_phase_greater_than_address_low_bits='1' then
+ report "DISPATCH: Supplying data from partially read data block. Value is $"
+ & to_hstring(block_data(to_integer(address(4 downto 3)))(to_integer(address(2 downto 0))))
+ & " ( from (" & integer'image(to_integer(address(4 downto 3)))
+ & ")(" & integer'image(to_integer(address(2 downto 0)));
+ report "asserting data_ready_strobe";
+ read_request_delatch <= '1';
+ data_ready_strobe <= '1';
+ data_ready_strobe_hold <= '1';
+ rdata <= block_data(to_integer(address(4 downto 3)))(to_integer(address(2 downto 0)));
+ if rdata_16en='1' then
+ rdata_hi <= block_data(to_integer(address(4 downto 3)))(to_integer(address(2 downto 0))+1);
+ end if;
+ last_request_toggle <= request_toggle;
+
+ -- Also push the whole cache line equivalent to
+ -- slow_devices to help it optimise linear reads
+ report "DISPATCH: byte_phase = " & integer'image(to_integer(byte_phase))
+ & ", address=$" & to_hstring(address);
+ if byte_phase_greater_than_address_end_of_row = '1' then
+ report "DISPATCH: Pushing block line to current_cache_line";
+ current_cache_line_drive <= block_data(to_integer(address(4 downto 3)));
+ current_cache_line_address_drive(26 downto 5) <= block_address(26 downto 5);
+ current_cache_line_address_drive(4 downto 3) <= address(4 downto 3);
+ current_cache_line_valid_drive <= '1';
+ else
+ report "DISPATCH: " & integer'image(to_integer(byte_phase)) & " not > " &
+ integer'image(to_integer(address(4 downto 3)&"111"));
+ end if;
+ end if;
+ end if;
+
+ elsif read_request_prev='1' and (not is_expected_to_respond) and (not is_vic_fetch) then
+ report "DISPATCH: Aborting pre-fetch due to incoming read request";
+ state <= ReadAbort;
+ end if;
+ end if;
+ -- Because we can now abort at any time, we can pretend we are
+ -- not busy. We are just filling in time...
+ if busy_internal = '1' then
+ report "DISPATCH: Clearing busy during tail of pre-fetch";
+ end if;
+ busy_internal <= '0';
+ end if;
+ -- After we have read the first 8 bytes, we know that we are no longer
+ -- required to provide any further direct output, so clear the
+ -- flag, so that the above logic can terminate a pre-fetch when required.
+ if byte_phase = 8 and (not is_vic_fetch) then
+ report "DISPATCH: Clearing is_expected_to_respond";
+ is_expected_to_respond <= false;
+ end if;
+ -- Clear busy flag as soon as we can, allowing for pipelining
+ -- through to and from slow_devices, so that we don't waste time,
+ -- but also that we avoid doing it too early and screwing things up.
+ if byte_phase = 4 then
+ if is_block_read and cache_enabled then
+ busy_internal <= '0';
+ end if;
+ end if;
+
+ hr_clk_phaseshift <= read_phase_shift xor hyperram1_select;
+
+ if hyperram0_select='1' then
+ last_rwds <= hr_rwds;
+ end if;
+ -- HyperRAM drives RWDS basically to follow the clock.
+ -- But first valid data is when RWDS goes high, so we have to
+ -- wait until we see it go high.
+-- report "DISPATCH watching for data: rwds=" & std_logic'image(hr_rwds) & ", clock=" & std_logic'image(hr_clock)
+-- & ", rwds seen=" & std_logic'image(hr_rwds_high_seen);
+
+ if ((hr_rwds='1') and (hyperram0_select='1')) then
+ hr_rwds_high_seen <= '1';
+-- if hr_rwds_high_seen = '0' then
+ -- report "DISPATCH saw hr_rwds go high at start of data stream";
+-- end if;
+ else
+ hr_rwds_high_seen <= '0';
+ end if;
+ if ((hr_rwds='1') and (hyperram0_select='1')) or (hr_rwds_high_seen='1') then
+ -- Data has arrived: Latch either odd or even byte
+ -- as required.
+ report "DISPATCH Saw read data = $" & to_hstring(hr_d);
+
+ -- Update cache
+ if (byte_phase < 32) and is_block_read and (not is_vic_fetch) then
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ block_data(to_integer(byte_phase(4 downto 3)))(to_integer(byte_phase(2 downto 0)))
+ <= hr_d;
+ end if;
+ show_block := true;
+ end if;
+ if (byte_phase < 8) then
+ -- Store the bytes in the cache row
+ if is_vic_fetch then
+ if hyperram0_select='1' then
+ viciv_data_buffer(to_integer(byte_phase)) <= hr_d;
+ end if;
+ -- We load the data here 2x faster than it is sent to the VIC-IV
+ -- so we can start transmitting immediately, to minimise latency
+ if byte_phase = 0 then
+ report "VIC: Indicating buffer readiness";
+ viciv_buffer_toggle <= not viciv_buffer_toggle;
+ end if;
+ elsif hyperram_access_address_matches_cache_row0 = '1' then
+ cache_row0_valids(to_integer(byte_phase)) <= '1';
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ cache_row0_data(to_integer(byte_phase)) <= hr_d;
+ end if;
+ show_cache0 := true;
+ elsif hyperram_access_address_matches_cache_row1 = '1' then
+ cache_row1_valids(to_integer(byte_phase)) <= '1';
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ cache_row1_data(to_integer(byte_phase)) <= hr_d;
+ end if;
+ show_cache1 := true;
+ elsif random_bits(1) = '0' then
+ report "Zeroing cache_row0_valids";
+ cache_row0_valids <= (others => '0');
+ cache_row0_address <= hyperram_access_address(26 downto 3);
+ hyperram_access_address_matches_cache_row0 <= '1';
+ cache_row0_valids(to_integer(byte_phase)) <= '1';
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ cache_row0_data(to_integer(byte_phase)) <= hr_d;
+ end if;
+ show_cache0 := true;
+ else
+ report "Zeroing cache_row1_valids";
+ cache_row1_valids <= (others => '0');
+ cache_row1_address <= hyperram_access_address(26 downto 3);
+ hyperram_access_address_matches_cache_row1 <= '1';
+ cache_row1_valids(to_integer(byte_phase)) <= '1';
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ cache_row1_data(to_integer(byte_phase)) <= hr_d;
+ end if;
+ show_cache1 := true;
+ end if;
+ elsif (byte_phase = 8) and is_expected_to_respond then
+ -- Export the appropriate cache line to slow_devices
+ if hyperram_access_address_matches_cache_row0 = '1' and cache_enabled and (not is_vic_fetch) then
+ if cache_row0_valids = x"FF" then
+ end if;
+ elsif hyperram_access_address_matches_cache_row1 = '1' and cache_enabled and (not is_vic_fetch) then
+ if cache_row1_valids = x"FF" then
+ current_cache_line_drive <= cache_row1_data;
+ current_cache_line_address_drive(26 downto 3) <= hyperram_access_address(26 downto 3);
+ current_cache_line_valid_drive <= '1';
+ end if;
+ end if;
+ end if;
+
+ -- Quickly return the correct byte
+ if to_integer(byte_phase) = (to_integer(hyperram_access_address(2 downto 0))+0) and is_expected_to_respond
+ and (not is_vic_fetch) then
+ if hyperram0_select='1' then
+ report "DISPATCH: Returning freshly read data = $" & to_hstring(hr_d) & ", hyperram0_select=" & std_logic'image(hyperram0_select) & ", hyperram1_select=" & std_logic'image(hyperram1_select);
+ if rdata_16en='1' and byte_phase(0)='1' then
+ rdata_hi <= hr_d;
+ else
+ rdata <= hr_d;
+ end if;
+ end if;
+ report "hr_return='1'";
+ report "hr_return='0'";
+ if rdata_16en='0' or byte_phase(0)='1' then
+ report "asserting data_ready_strobe";
+ data_ready_strobe <= '1';
+ data_ready_strobe_hold <= '1';
+ end if;
+ end if;
+ report "byte_phase = " & integer'image(to_integer(byte_phase));
+ if ((byte_phase = 7) and (is_block_read=false))
+ or (byte_phase = 31) then
+ rwr_counter <= rwr_delay;
+ rwr_waiting <= '1';
+ report "returning to idle";
+ last_request_toggle <= request_toggle;
+ state <= Idle;
+ hr_cs0 <= '1';
+ hr_clk_phaseshift <= write_phase_shift;
+ if is_block_read then
+ block_valid <= '1';
+ end if;
+ is_prefetch <= false;
+ is_expected_to_respond <= false;
+ else
+ byte_phase <= byte_phase + 1;
+ end if;
+ end if;
+ when HyperRAMReadWaitSlow =>
+ hr_rwds <= 'Z';
+ report "Presenting tri-state on hr_d";
+ hr_d <= (others => 'Z');
+
+ if hyperram0_select='1' then
+ hr_d_last <= hr_d;
+ end if;
+
+ pause_phase <= not pause_phase;
+
+ -- Abort memory pre-fetching if we are asked to do something
+ -- XXX unless it is for data that would be pre-fetched?
+ if is_block_read and (not is_expected_to_respond) and (not is_vic_fetch) then
+ if ( write_request='1')
+ -- If a new read is on the same cache line as the last, then
+ -- assume whatever read we are doing now will satisfy it
+-- or (read_request='1' and address(26 downto 3) /= ram_address(26 downto 3))
+ or (read_request='1')
+ then
+ report "DISPATCH: Aborting pre-fetch due to incoming request";
+ state <= ReadAbort;
+ end if;
+ -- Because we can now abort at any time, we can pretend we are
+ -- not busy. We are just filling in time...
+ if busy_internal = '1' then
+ report "DISPATCH: Clearing busy during tail of pre-fetch";
+ end if;
+ if cache_enabled then
+ busy_internal <= '0';
+ end if;
+ end if;
+ -- After we have read the first 8 bytes, we know that we are no longer
+ -- required to provide any further direct output, so clear the
+ -- flag, so that the above logic can terminate a pre-fetch when required.
+ if byte_phase = 8 then
+ report "DISPATCH: Clearing is_expected_to_respond";
+ is_expected_to_respond <= false;
+ end if;
+
+ if pause_phase = '1' then
+ null;
+ else
+ hr_clk_phaseshift <= read_phase_shift xor hyperram1_select;
+ if countdown_is_zero = '0' then
+ countdown <= countdown - 1;
+ end if;
+ if countdown = 1 then
+ countdown_is_zero <= '1';
+ end if;
+ if countdown_is_zero = '1' then
+ -- Timed out waiting for read -- so return anyway, rather
+ -- than locking the machine hard forever.
+ rdata_hi <= x"DD";
+ rdata <= x"DD";
+ rdata(0) <= data_ready_toggle;
+ rdata(1) <= busy_internal;
+ report "asserting data_ready_strobe";
+ data_ready_strobe <= '1';
+ data_ready_strobe_hold <= '1';
+ rwr_counter <= rwr_delay;
+ rwr_waiting <= '1';
+ report "returning to idle";
+ state <= Idle;
+ hr_clk_phaseshift <= write_phase_shift;
+ end if;
+
+ if hyperram0_select='1' then
+ last_rwds <= hr_rwds;
+ end if;
+ -- HyperRAM drives RWDS basically to follow the clock.
+ -- But first valid data is when RWDS goes high, so we have to
+ -- wait until we see it go high.
+-- report "DISPATCH watching for data: rwds=" & std_logic'image(hr_rwds) & ", clock=" & std_logic'image(hr_clock)
+-- & ", rwds seen=" & std_logic'image(hr_rwds_high_seen);
+
+ if (hr_rwds='1') and (hyperram0_select='1')
+ then
+ hr_rwds_high_seen <= '1';
+ else
+ hr_rwds_high_seen <= '0';
+-- if hr_rwds_high_seen = '0' then
+ -- report "DISPATCH saw hr_rwds go high at start of data stream";
+-- end if;
+ end if;
+ if ((hr_rwds='1') and (hyperram0_select='1'))
+ or (hr_rwds_high_seen='1') then
+ -- Data has arrived: Latch either odd or even byte
+ -- as required.
+-- report "DISPATCH Saw read data = $" & to_hstring(hr_d);
+
+ -- Update cache
+ if (byte_phase < 32) and is_block_read and (not is_vic_fetch) then
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ block_data(to_integer(byte_phase(4 downto 3)))(to_integer(byte_phase(2 downto 0)))
+ <= hr_d;
+ end if;
+ show_block := true;
+ end if;
+ if byte_phase < 8 then
+ -- Store the bytes in the cache row
+ if is_vic_fetch then
+ if hyperram0_select='1' then
+ viciv_data_buffer(to_integer(byte_phase)) <= hr_d;
+ end if;
+ -- We load the data here 2x faster than it is sent to the VIC-IV
+ -- so we can start transmitting immediately, to minimise latency
+ if byte_phase = 0 then
+ report "VIC: Indicating buffer readiness";
+ viciv_buffer_toggle <= not viciv_buffer_toggle;
+ end if;
+ elsif hyperram_access_address_matches_cache_row0 = '1' then
+ cache_row0_valids(to_integer(byte_phase)) <= '1';
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ cache_row0_data(to_integer(byte_phase)) <= hr_d;
+ end if;
+ show_cache0 := true;
+ elsif hyperram_access_address_matches_cache_row1 = '1' then
+ cache_row1_valids(to_integer(byte_phase)) <= '1';
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ cache_row1_data(to_integer(byte_phase)) <= hr_d;
+ end if;
+ show_cache1 := true;
+ elsif random_bits(1) = '0' then
+ report "Zeroing cache_row0_valids";
+ cache_row0_valids <= (others => '0');
+ cache_row0_address <= hyperram_access_address(26 downto 3);
+ hyperram_access_address_matches_cache_row0 <= '1';
+ cache_row0_valids(to_integer(byte_phase)) <= '1';
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ cache_row0_data(to_integer(byte_phase)) <= hr_d;
+ end if;
+ show_cache0 := true;
+ else
+ report "Zeroing cache_row1_valids";
+ cache_row1_valids <= (others => '0');
+ cache_row1_address <= hyperram_access_address(26 downto 3);
+ hyperram_access_address_matches_cache_row1 <= '1';
+ cache_row1_valids(to_integer(byte_phase)) <= '1';
+ report "hr_sample='1'";
+ report "hr_sample='0'";
+ if hyperram0_select='1' then
+ cache_row1_data(to_integer(byte_phase)) <= hr_d;
+ end if;
+ show_cache1 := true;
+ end if;
+ else
+ -- Export the appropriate cache line to slow_devices
+ if hyperram_access_address_matches_cache_row0 = '1' and cache_enabled and (not is_vic_fetch) then
+ if cache_row0_valids = x"FF" then
+ current_cache_line_drive <= cache_row0_data;
+ current_cache_line_address_drive(26 downto 3) <= hyperram_access_address(26 downto 3);
+ current_cache_line_valid_drive <= '1';
+ end if;
+ elsif hyperram_access_address_matches_cache_row1 = '1' and cache_enabled and (not is_vic_fetch) then
+ if cache_row1_valids = x"FF" then
+ current_cache_line_drive <= cache_row1_data;
+ current_cache_line_address_drive(26 downto 3) <= hyperram_access_address(26 downto 3);
+ current_cache_line_valid_drive <= '1';
+ end if;
+ end if;
+ end if;
+
+ -- Quickly return the correct byte
+ if byte_phase = hyperram_access_address_read_time_adjusted and (not is_vic_fetch) then
+ if hyperram0_select='1' then
+ report "DISPATCH: Returning freshly read data = $" & to_hstring(hr_d);
+ rdata <= hr_d;
+ end if;
+ report "hr_return='1'";
+ report "hr_return='0'";
+ if rdata_16en='0' then
+ report "asserting data_ready_strobe on low byte";
+ data_ready_strobe <= '1';
+ data_ready_strobe_hold <= '1';
+ end if;
+ end if;
+ if byte_phase = (hyperram_access_address_read_time_adjusted+1) and (not is_vic_fetch) and (rdata_16en='1') then
+ if hyperram0_select='1' then
+ report "DISPATCH: Returning freshly read high-byte data = $" & to_hstring(hr_d);
+ rdata_hi <= hr_d;
+ end if;
+ report "hr_return='1'";
+ report "hr_return='0'";
+
+ report "asserting data_ready_strobe on high byte";
+ data_ready_strobe <= '1';
+ data_ready_strobe_hold <= '1';
+
+ end if;
+ report "byte_phase = " & integer'image(to_integer(byte_phase));
+ if (byte_phase = seven_plus_read_time_adjust and is_block_read=false)
+ or (byte_phase = thirtyone_plus_read_time_adjust and is_block_read=true)
+ then
+ rwr_counter <= rwr_delay;
+ rwr_waiting <= '1';
+ report "returning to idle";
+ state <= Idle;
+ hr_cs0 <= '1';
+ hr_clk_phaseshift <= write_phase_shift;
+ else
+ byte_phase <= byte_phase + 1;
+ end if;
+ end if;
+ end if;
+ end case;
+ end if;
+
+ end process;
+end gothic;
+
+
diff --git a/vhdl/hw/MEGA65/hdmi_tx_encoder.vhd b/vhdl/hw/MEGA65/hdmi_tx_encoder.vhd
new file mode 100644
index 00000000..6758c654
--- /dev/null
+++ b/vhdl/hw/MEGA65/hdmi_tx_encoder.vhd
@@ -0,0 +1,177 @@
+--------------------------------------------------------------------------------
+-- hdmi_tx_encoder.vhd --
+-- HDMI TMDS encoder channel. --
+--------------------------------------------------------------------------------
+-- (C) Copyright 2020 Adam Barnes --
+-- This file is part of The Tyto Project. The Tyto Project is free software: --
+-- you can redistribute it and/or modify it under the terms of the GNU Lesser --
+-- General Public License as published by the Free Software Foundation, --
+-- either version 3 of the License, or (at your option) any later version. --
+-- The Tyto Project is distributed in the hope that it will be useful, but --
+-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public --
+-- License for more details. You should have received a copy of the GNU --
+-- Lesser General Public License along with The Tyto Project. If not, see --
+-- https://www.gnu.org/licenses/. --
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+
+package hdmi_tx_encoder_pkg is
+
+ constant ENC_DVI : std_logic_vector(1 downto 0) := "00";
+ constant ENC_GB_VIDEO : std_logic_vector(1 downto 0) := "01";
+ constant ENC_DATA : std_logic_vector(1 downto 0) := "10";
+ constant ENC_GB_DATA : std_logic_vector(1 downto 0) := "11";
+
+end package hdmi_tx_encoder_pkg;
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+use work.hdmi_tx_encoder_pkg.all;
+
+entity hdmi_tx_encoder is
+ generic (
+ channel : integer range 0 to 2 -- affects encoding of guard bands
+ );
+ port (
+ rst : in std_logic; -- synchronous reset
+ clk : in std_logic; -- pixel clock
+ de : in std_logic; -- pixel data enable
+ p : in std_logic_vector(7 downto 0); -- pixel data
+ enc : in std_logic_vector(1 downto 0); -- encoding type (non-video)
+ c : in std_logic_vector(1 downto 0); -- control
+ d : in std_logic_vector(3 downto 0); -- aux data (for data islands)
+ q : out std_logic_vector(9 downto 0) -- TMDS encoded output
+ );
+end entity hdmi_tx_encoder;
+
+-------------------------------------------------------------------------------
+
+architecture synth of hdmi_tx_encoder is
+
+ -- TMDS encoding related
+ signal n1p : unsigned(3 downto 0); -- 0 to 8
+ signal q_m : std_logic_vector(8 downto 0);
+ signal n0q_m : unsigned(3 downto 0); -- 0 to 8
+ signal n1q_m : unsigned(3 downto 0); -- 0 to 8
+ signal diff01_5 : signed(4 downto 0); -- -8 to +8 (even)
+ signal diff10_5 : signed(4 downto 0); -- -8 to +8 (even)
+ alias diff01 : signed(3 downto 0) is diff01_5(4 downto 1); -- -4 to +4
+ alias diff10 : signed(3 downto 0) is diff10_5(4 downto 1); -- -4 to +4
+ signal cnt : signed(3 downto 0); -- -4 to +4
+
+ -- function to count bits
+ function Nv(x : std_logic_vector; v : std_logic) return integer is
+ variable n : integer;
+ begin
+ n := 0;
+ for i in 0 to x'length-1 loop
+ if x(i) = v then n := n+1; end if;
+ end loop;
+ return n;
+ end function Nv;
+
+begin
+
+ n1p <= to_unsigned(Nv(p,'1'),4);
+ q_m(0) <= p(0);
+ q_m(8 downto 1) <=
+ '0' & (q_m(6 downto 0) xnor p(7 downto 1)) when n1p > x"4" or (n1p = x"4" and p(0) = '0') else
+ '1' & (q_m(6 downto 0) xor p(7 downto 1));
+ n0q_m <= to_unsigned(Nv(q_m(7 downto 0),'0'),4);
+ n1q_m <= to_unsigned(Nv(q_m(7 downto 0),'1'),4);
+ diff01_5 <= signed(resize(n0q_m,5)) - signed(resize(n1q_m,5)); -- difference between number of 1s and number of 0s
+ diff10_5 <= signed(resize(n1q_m,5)) - signed(resize(n0q_m,5)); -- is always even so drop LSB from count
+
+ process(clk)
+ begin
+ if rising_edge(clk) then
+ if rst = '1' then
+ q <= "1101010100";
+ cnt <= x"0";
+ else
+ if de = '1' then -- video
+ if (cnt = 0) or (n1q_m = n0q_m) then
+ q(9) <= not q_m(8);
+ q(8) <= q_m(8);
+ q(7 downto 0) <= q_m(7 downto 0) xor (7 downto 0 => not q_m(8));
+ if q_m(8) = '0' then
+ cnt <= cnt + diff01;
+ else
+ cnt <= cnt + diff10;
+ end if;
+ elsif (cnt > x"0" and n1q_m > n0q_m) or (cnt < x"0" and n0q_m > n1q_m) then
+ q(9) <= '1';
+ q(8) <= q_m(8);
+ q(7 downto 0) <= not q_m(7 downto 0);
+ cnt <= cnt + signed("000" & q_m(8 downto 8)) + diff01;
+ else
+ q(9) <= '0';
+ q(8) <= q_m(8);
+ q(7 downto 0) <= q_m(7 downto 0);
+ cnt <= cnt - signed("000" & not q_m(8 downto 8)) + diff10;
+ end if;
+ else -- not video
+ cnt <= x"0";
+ case enc is
+ when ENC_DVI => -- control
+ case c is
+ when "00" => q <= "1101010100";
+ when "01" => q <= "0010101011";
+ when "10" => q <= "0101010100";
+ when "11" => q <= "1010101011";
+ when others => q <= "XXXXXXXXXX";
+ end case;
+ when ENC_GB_VIDEO => -- video guard band
+ case channel is
+ when 0 => q <= "1011001100";
+ when 1 => q <= "0100110011";
+ when 2 => q <= "1011001100";
+ when others => q <= "XXXXXXXXXX";
+ end case;
+ when ENC_DATA => -- data island contents
+ case d is
+ when "0000" => q <= "1010011100";
+ when "0001" => q <= "1001100011";
+ when "0010" => q <= "1011100100";
+ when "0011" => q <= "1011100010";
+ when "0100" => q <= "0101110001";
+ when "0101" => q <= "0100011110";
+ when "0110" => q <= "0110001110";
+ when "0111" => q <= "0100111100";
+ when "1000" => q <= "1011001100";
+ when "1001" => q <= "0100111001";
+ when "1010" => q <= "0110011100";
+ when "1011" => q <= "1011000110";
+ when "1100" => q <= "1010001110";
+ when "1101" => q <= "1001110001";
+ when "1110" => q <= "0101100011";
+ when others => q <= "1011000011";
+ end case;
+ when ENC_GB_DATA => -- data island guard band
+ case channel is
+ when 2 => q <= "0100110011";
+ when 1 => q <= "0100110011";
+ when 0 => -- special rules for channel 0...
+ case c is
+ when "00" => q <= "1010001110";
+ when "01" => q <= "1001110001";
+ when "10" => q <= "0101100011";
+ when "11" => q <= "1011000011";
+ when others => q <= "XXXXXXXXXX";
+ end case;
+ end case;
+ when others =>
+ q <= "XXXXXXXXXX";
+ end case;
+ end if;
+ end if;
+ end if;
+ end process;
+
+end architecture synth;
diff --git a/vhdl/hw/MEGA65/hyperram_ctl_r6.vhd b/vhdl/hw/MEGA65/hyperram_ctl_r6.vhd
new file mode 100644
index 00000000..0e2f913f
--- /dev/null
+++ b/vhdl/hw/MEGA65/hyperram_ctl_r6.vhd
@@ -0,0 +1,346 @@
+-- QNICE-MEGA65 HyperRAM controller
+-- done by sy2002 in April to August 2020
+--
+-- Wraps the MEGA65 HyperRAM controller so that it can be connected
+-- to the QNICE CPU's data bus and controled via MMIO.
+-- CPU wait states are automatically inserted by the HyperRAM controller.
+-- data_out goes to zero, if not enabled
+--
+-- Registers:
+--
+-- Register $FFF0: Low word of address (15 downto 0)
+-- Register $FFF1: High word of address (26 downto 16)
+-- Register $FFF2: 8-bit data in/out (native mode: HyperRAM is 8-bit)
+-- Register $FFF3: 16-bit data in/out (leads to address being multiplied by two)
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity hyperram_ctl is
+port(
+ -- HyperRAM needs a base clock and then one with 2x speed and one with 4x speed
+ clk : in std_logic; -- currently 50 MHz QNICE system clock
+ clk2x : in std_logic; -- 100 Mhz
+ clk4x : in std_logic; -- 200 Mhz
+
+ reset : in std_logic;
+
+ -- connect to CPU's data bus (data high impedance when all reg_* are 0)
+ hram_en : in std_logic;
+ hram_we : in std_logic;
+ hram_reg : in std_logic_vector(3 downto 0);
+ hram_cpu_ws : out std_logic; -- insert CPU wait states (aka WAIT_FOR_DATA)
+ data_in : in std_logic_vector(15 downto 0);
+ data_out : out std_logic_vector(15 downto 0);
+
+ -- hardware connections
+ hr_d : inout unsigned(7 downto 0); -- Data/Address
+ hr_rwds : inout std_logic; -- RW Data strobe
+ hr_reset : out std_logic; -- Active low RESET line to HyperRAM
+ hr_clk_p : out std_logic;
+ hr_cs0 : out std_logic
+);
+end hyperram_ctl;
+
+architecture beh of hyperram_ctl is
+
+component hyperram is
+ port ( pixelclock : in std_logic;
+ clock163 : in std_logic;
+ clock325 : in std_logic;
+
+ -- Simple counter for number of requests received
+ request_counter : out std_logic;
+
+ read_request : in std_logic;
+ write_request : in std_logic;
+ address : in unsigned(26 downto 0);
+ wdata : in unsigned(7 downto 0);
+
+ rdata : out unsigned(7 downto 0);
+ data_ready_strobe : out std_logic;
+ busy : out std_logic;
+
+ -- 16-bit enhancements
+ wen_hi : in std_logic;
+ wen_lo : in std_logic;
+ wdata_hi : in unsigned(7 downto 0);
+ rdata_hi : out unsigned(7 downto 0);
+ rdata_16en : in std_logic;
+
+ -- HyperRAM hardware signals
+ hr_d : inout unsigned(7 downto 0);
+ hr_rwds : inout std_logic;
+ hr_reset : out std_logic;
+ hr_clk_p : out std_logic;
+ hr_cs0 : out std_logic
+ );
+end component;
+
+-- HyperRAM control and data signals
+signal hram_read_request : std_logic;
+signal hram_write_request : std_logic;
+signal hram_address : unsigned(26 downto 0);
+signal hram_wdata_ff : unsigned(15 downto 0) := (others => '0');
+signal hram_rdata : unsigned(15 downto 0);
+signal hram_data_ready_strobe : std_logic;
+signal hram_busy : std_logic;
+signal hram_wen_hi : std_logic;
+signal hram_wen_lo : std_logic;
+signal hram_rdata_16en : std_logic;
+
+-- Controller logic
+signal hram_addr_lo_ff : unsigned(15 downto 0) := (others => '0');
+signal hram_addr_hi_ff : unsigned(15 downto 0) := (others => '0');
+signal hram_rdata_ff : unsigned(15 downto 0) := (others => '0');
+signal hram_16bit_ff : std_logic := '0';
+
+
+type tHRAM_FSM_States is ( s_idle,
+
+ s_read_start,
+ s_read_waitfordata,
+ s_read_waitforcpu,
+
+ s_write1,
+ s_write2
+ );
+
+signal state_ff : tHRAM_FSM_States := s_idle;
+signal state_next : tHRAM_FSM_States;
+
+signal fsm_state_next : tHRAM_FSM_States;
+signal fsm_hram_rdata : unsigned(15 downto 0);
+signal fsm_hram_16bit : std_logic;
+
+
+signal dbg_chkadd_ff : unsigned(15 downto 0) := x"0000";
+signal fsm_chkadd : unsigned(15 downto 0);
+
+begin
+
+ HRAM : hyperram
+ port map (
+ pixelclock => clk,
+ clock163 => clk2x,
+ clock325 => clk4x,
+ read_request => hram_read_request,
+ write_request => hram_write_request,
+ address => hram_address,
+ wdata => hram_wdata_ff(7 downto 0),
+ wdata_hi => hram_wdata_ff(15 downto 8),
+ rdata => hram_rdata(7 downto 0),
+ rdata_hi => hram_rdata(15 downto 8),
+ wen_hi => hram_wen_hi,
+ wen_lo => hram_wen_lo,
+ rdata_16en => hram_rdata_16en,
+ data_ready_strobe => hram_data_ready_strobe,
+ busy => hram_busy,
+ hr_d => hr_d,
+ hr_rwds => hr_rwds,
+ hr_reset => hr_reset,
+ hr_clk_p => hr_clk_p,
+ hr_cs0 => hr_cs0
+ );
+
+ fsm_advance_state : process(clk, reset)
+ begin
+ if reset = '1' then
+ state_ff <= s_idle;
+ hram_rdata_ff <= x"0000";
+ hram_16bit_ff <= '0';
+
+ dbg_chkadd_ff <= (others => '0');
+ else
+ if rising_edge(clk) then
+ state_ff <= fsm_state_next;
+ hram_rdata_ff <= fsm_hram_rdata;
+ hram_16bit_ff <= fsm_hram_16bit;
+
+ dbg_chkadd_ff <= dbg_chkadd_ff + fsm_chkadd;
+ end if;
+ end if;
+ end process;
+
+ fsm_next_state_decode : process (state_ff)
+ begin
+ case state_ff is
+ when s_idle => state_next <= s_idle;
+
+ when s_read_start => state_next <= s_read_waitfordata;
+ when s_read_waitfordata => state_next <= s_read_waitforcpu;
+ when s_read_waitforcpu => state_next <= s_idle;
+
+ when s_write1 => state_next <= s_write2;
+ when s_write2 => state_next <= s_idle;
+ end case;
+ end process;
+
+ fsm_output_decode : process(state_ff, state_next, hram_rdata_ff, hram_rdata, hram_data_ready_strobe, hram_busy,
+ hram_16bit_ff, hram_reg, hram_en, hram_we)
+ begin
+ hram_cpu_ws <= '0';
+ hram_read_request <= '0';
+ hram_write_request <= '0';
+ hram_wen_lo <= '1';
+ hram_wen_hi <= '0';
+ hram_rdata_16en <= '0';
+
+ fsm_state_next <= state_next;
+ fsm_chkadd <= x"0000";
+ fsm_hram_rdata <= hram_rdata_ff;
+ fsm_hram_16bit <= hram_16bit_ff;
+
+ case state_ff is
+ when s_idle =>
+
+ -- detect 16-bit mode
+ if hram_reg = x"3" then
+ fsm_hram_16bit <= '1';
+ else
+ fsm_hram_16bit <= '0';
+ end if;
+
+ -- start read process
+ if hram_en = '1' and hram_we = '0' and (hram_reg = x"2" or hram_reg = x"3") then
+ hram_cpu_ws <= '1';
+ if hram_busy = '0' then
+ fsm_state_next <= s_read_start;
+ end if;
+
+ -- start write process
+ elsif hram_en = '1' and hram_we = '1' and (hram_reg = x"2" or hram_reg = x"3") then
+ hram_cpu_ws <= '1';
+ if hram_busy = '0' then
+ fsm_state_next <= s_write1;
+ end if;
+ end if;
+
+ -- READING
+
+ when s_read_start =>
+ hram_cpu_ws <= '1';
+ hram_read_request <= '1';
+ hram_rdata_16en <= hram_16bit_ff;
+ fsm_chkadd <= x"0100";
+ if hram_busy = '0' and hram_data_ready_strobe = '0' then
+ fsm_state_next <= s_read_start;
+ else
+ hram_read_request <= '0';
+ hram_rdata_16en <= '0'; -- is this correct?
+ if hram_data_ready_strobe = '1' then
+ fsm_hram_rdata <= hram_rdata;
+ fsm_state_next <= s_read_waitforcpu;
+ end if;
+ end if;
+
+ when s_read_waitfordata =>
+ hram_cpu_ws <= '1';
+ fsm_chkadd <= x"0001";
+ if hram_data_ready_strobe = '1' then
+ fsm_hram_rdata <= hram_rdata;
+ else
+ fsm_state_next <= s_read_waitfordata;
+ end if;
+
+ when s_read_waitforcpu =>
+ -- wait for CPU to deassert reading so that we can synchronously reset start_read_ff
+ if hram_en = '1' and hram_we = '0' and (hram_reg = x"2" or hram_reg = x"3") then
+ fsm_state_next <= s_read_waitforcpu;
+ end if;
+
+ -- WRITING
+
+ when s_write1 =>
+ hram_write_request <= '1';
+ hram_wen_hi <= hram_16bit_ff;
+
+ -- TODO: Check, if necessary
+ when s_write2 =>
+ null;
+
+ end case;
+ end process;
+
+ read_registers : process(hram_en, hram_we, hram_reg, hram_data_ready_strobe, hram_busy,
+ hram_address, hram_rdata_ff, dbg_chkadd_ff)
+ begin
+ data_out <= (others => '0');
+
+ if hram_en = '1' and hram_we = '0' then
+ case hram_reg is
+
+ -- read low word of address
+ when x"0" => data_out <= std_logic_vector(hram_address(15 downto 0));
+
+ -- read (partial) high word of address
+ when x"1" => data_out <= std_logic_vector("00000" & hram_address(26 downto 16));
+
+ -- read 8-bit data
+ when x"2" =>
+ data_out <= x"00" & std_logic_vector(hram_rdata_ff(7 downto 0));
+
+ -- read 16-bit data
+ when x"3" =>
+ data_out <= std_logic_vector(hram_rdata_ff);
+
+ -- debug
+ when x"6" =>
+ data_out <= std_logic_vector(dbg_chkadd_ff);
+
+ when others =>
+ data_out <= (others => '0');
+ end case;
+ end if;
+ end process;
+
+ write_registers : process(clk, reset)
+ begin
+ if reset = '1' then
+ hram_addr_lo_ff <= (others => '0');
+ hram_addr_hi_ff <= (others => '0');
+ hram_wdata_ff <= (others => '0');
+ else
+ if falling_edge(clk) then
+ if hram_en = '1' and hram_we = '1' then
+ case hram_reg is
+
+ -- write low word of address
+ when x"0" => hram_addr_lo_ff <= unsigned(data_in);
+
+ -- write high word of address
+ when x"1" => hram_addr_hi_ff <= unsigned(data_in);
+
+ -- write 8-bit data register
+ when x"2" =>
+ hram_wdata_ff <= x"00" & unsigned(data_in(7 downto 0));
+
+ -- write 16-bit data register
+ when x"3" =>
+ hram_wdata_ff <= unsigned(data_in);
+
+ when others => null;
+ end case;
+ end if;
+ end if;
+ end if;
+ end process;
+
+ calc_hram_address : process(hram_addr_hi_ff, hram_addr_lo_ff, hram_16bit_ff, fsm_hram_16bit)
+ begin
+ -- 8-bit mode: address = plain concatenation of hi and low word
+ if hram_16bit_ff = '0' and fsm_hram_16bit = '0' then
+ hram_address <= hram_addr_hi_ff(10 downto 0) & hram_addr_lo_ff(15 downto 0);
+
+ -- 16-bit mode: address is x2
+ -- multiplication is done by a shift left which itself is done by wiring the
+ -- two source flip flops appropriately, so that everything happens combinatorically in "no time"
+ else
+ hram_address(26 downto 17) <= hram_addr_hi_ff(9 downto 0);
+ hram_address(16 downto 1) <= hram_addr_lo_ff(15 downto 0);
+ hram_address(0) <= '0';
+ end if;
+ end process;
+
+end beh;
diff --git a/vhdl/hw/MEGA65/serialiser_10to1_selectio.vhd b/vhdl/hw/MEGA65/serialiser_10to1_selectio.vhd
new file mode 100644
index 00000000..6da533a0
--- /dev/null
+++ b/vhdl/hw/MEGA65/serialiser_10to1_selectio.vhd
@@ -0,0 +1,142 @@
+--------------------------------------------------------------------------------
+-- serialiser_10to1_selectio.vhd --
+-- 10:1 serialiser driving SelectIO differential output pair. --
+--------------------------------------------------------------------------------
+-- (C) Copyright 2020 Adam Barnes --
+-- This file is part of The Tyto Project. The Tyto Project is free software: --
+-- you can redistribute it and/or modify it under the terms of the GNU Lesser --
+-- General Public License as published by the Free Software Foundation, --
+-- either version 3 of the License, or (at your option) any later version. --
+-- The Tyto Project is distributed in the hope that it will be useful, but --
+-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public --
+-- License for more details. You should have received a copy of the GNU --
+-- Lesser General Public License along with The Tyto Project. If not, see --
+-- https://www.gnu.org/licenses/. --
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+entity serialiser_10to1_selectio is
+ port (
+ rst : in std_logic; -- reset
+ clk : in std_logic; -- parallel data clcok
+ clk_x5 : in std_logic; -- serialiser DDR clock
+ d : in std_logic_vector(9 downto 0); -- input parallel data
+ out_p : out std_logic; -- output serial data
+ out_n : out std_logic -- "
+ );
+end entity serialiser_10to1_selectio;
+
+architecture synth of serialiser_10to1_selectio is
+
+ signal s1 : std_logic;
+ signal s2 : std_logic;
+ signal q : std_logic;
+
+begin
+
+ -- serialiser (master)
+ U_SER_M: oserdese2
+ generic map (
+ data_rate_oq => "DDR", -- DDR, SDR
+ data_rate_tq => "SDR", -- DDR, BUF, SDR
+ data_width => 10, -- Parallel data width (2-8,10,14)
+ init_oq => '0', -- Initial value of OQ output (1'b0,1'b1)
+ init_tq => '0', -- Initial value of TQ output (1'b0,1'b1)
+ serdes_mode => "MASTER", -- MASTER, SLAVE
+ srval_oq => '0', -- OQ output value when SR is used (1'b0,1'b1)
+ srval_tq => '0', -- TQ output value when SR is used (1'b0,1'b1)
+ tbyte_ctl => "FALSE", -- Enable tristate byte operation (FALSE, TRUE)
+ tbyte_src => "FALSE", -- Tristate byte source (FALSE, TRUE)
+ tristate_width => 1 -- 3-state converter width (1,4)
+ )
+ port map (
+ ofb => open, -- 1-bit output: Feedback path for data
+ oq => q, -- 1-bit output: Data path output
+ shiftout1 => open, -- SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
+ shiftout2 => open,
+ tbyteout => open, -- 1-bit output: Byte group tristate
+ tfb => open, -- 1-bit output: 3-state control
+ tq => open, -- 1-bit output: 3-state control
+ clk => clk_x5, -- 1-bit input: High speed clock
+ clkdiv => clk, -- 1-bit input: Divided clock
+ d1 => d(0), -- D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
+ d2 => d(1),
+ d3 => d(2),
+ d4 => d(3),
+ d5 => d(4),
+ d6 => d(5),
+ d7 => d(6),
+ d8 => d(7),
+ oce => '1', -- 1-bit input: Output data clock enable
+ rst => rst, -- 1-bit input: Reset
+ shiftin1 => s1, -- SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
+ shiftin2 => s2,
+ t1 => '0', -- T1 - T4: 1-bit (each) input: Parallel 3-state inputs
+ t2 => '0',
+ t3 => '0',
+ t4 => '0',
+ tbytein => '0', -- 1-bit input: Byte group tristate
+ tce => '0' -- 1-bit input: 3-state clock enable
+ );
+
+ -- serialiser (slave)
+ U_SER_S: oserdese2
+ generic map (
+ data_rate_oq => "DDR", -- DDR, SDR
+ data_rate_tq => "SDR", -- DDR, BUF, SDR
+ data_width => 10, -- Parallel data width (2-8,10,14)
+ init_oq => '0', -- Initial value of OQ output (1'b0,1'b1)
+ init_tq => '0', -- Initial value of TQ output (1'b0,1'b1)
+ serdes_mode => "SLAVE", -- MASTER, SLAVE
+ srval_oq => '0', -- OQ output value when SR is used (1'b0,1'b1)
+ srval_tq => '0', -- TQ output value when SR is used (1'b0,1'b1)
+ tbyte_ctl => "FALSE", -- Enable tristate byte operation (FALSE, TRUE)
+ tbyte_src => "FALSE", -- Tristate byte source (FALSE, TRUE)
+ tristate_width => 1 -- 3-state converter width (1,4)
+ )
+ port map (
+ ofb => open, -- 1-bit output: Feedback path for data
+ oq => open, -- 1-bit output: Data path output
+ shiftout1 => s1, -- SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
+ shiftout2 => s2,
+ tbyteout => open, -- 1-bit output: Byte group tristate
+ tfb => open, -- 1-bit output: 3-state control
+ tq => open, -- 1-bit output: 3-state control
+ clk => clk_x5, -- 1-bit input: High speed clock
+ clkdiv => clk, -- 1-bit input: Divided clock
+ d1 => '0', -- D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
+ d2 => '0',
+ d3 => d(8),
+ d4 => d(9),
+ d5 => '0',
+ d6 => '0',
+ d7 => '0',
+ d8 => '0',
+ oce => '1', -- 1-bit input: Output data clock enable
+ rst => rst, -- 1-bit input: Reset
+ shiftin1 => '0', -- SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
+ shiftin2 => '0',
+ t1 => '0', -- T1 - T4: 1-bit (each) input: Parallel 3-state inputs
+ t2 => '0',
+ t3 => '0',
+ t4 => '0',
+ tbytein => '0', -- 1-bit input: Byte group tristate
+ tce => '0' -- 1-bit input: 3-state clock enable
+ );
+
+ -- differential output buffer
+ U_OBUF: obufds
+ port map (
+ i => q,
+ o => out_p,
+ ob => out_n
+ );
+
+end architecture synth;
diff --git a/vhdl/hw/MEGA65/sim/sim_hram_dbg_r6.vhd b/vhdl/hw/MEGA65/sim/sim_hram_dbg_r6.vhd
new file mode 100644
index 00000000..c69b6674
--- /dev/null
+++ b/vhdl/hw/MEGA65/sim/sim_hram_dbg_r6.vhd
@@ -0,0 +1,247 @@
+----------------------------------------------------------------------------------
+-- MEGA65 Top Module for simulation: HRAM debugging
+--
+-- done by sy2002 in June and August 2020
+----------------------------------------------------------------------------------
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+library UNISIM;
+use UNISIM.VCOMPONENTS.ALL;
+
+use work.env1_globals.all;
+
+entity MEGA65_HRAM_SIM is
+end MEGA65_HRAM_SIM;
+
+architecture beh of MEGA65_HRAM_SIM is
+
+-- CPU control signals
+signal cpu_addr : std_logic_vector(15 downto 0);
+signal cpu_data_in : std_logic_vector(15 downto 0);
+signal cpu_data_out : std_logic_vector(15 downto 0);
+signal cpu_data_dir : std_logic;
+signal cpu_data_valid : std_logic;
+signal cpu_wait_for_data : std_logic;
+signal cpu_halt : std_logic;
+
+-- MMIO control signals
+signal switch_reg_enable : std_logic;
+signal switch_data_out : std_logic_vector(15 downto 0);
+signal rom_enable : std_logic;
+signal rom_busy : std_logic;
+signal rom_data_out : std_logic_vector(15 downto 0);
+signal ram_enable : std_logic;
+signal ram_busy : std_logic;
+signal ram_data_out : std_logic_vector(15 downto 0);
+signal hram_en : std_logic;
+signal hram_we : std_logic;
+signal hram_reg : std_logic_vector(3 downto 0);
+signal hram_cpu_ws : std_logic;
+signal hram_data_out : std_logic_vector(15 downto 0);
+
+-- Main clock: 50 MHz as long as we did not solve the timing issues of the register file
+signal SLOW_CLOCK : std_logic;
+
+-- Pixelclock and fast clock for HRAM
+signal CLK1x : std_logic; -- 100 MHz clock created by mmcme2 for congruent phase
+signal CLK2x : std_logic; -- 4x SLOW_CLOCK = 200 MHz
+
+-- emulate the switches on the Nexys4 dev board to toggle VGA and PS/2
+signal SWITCHES : std_logic_vector(15 downto 0);
+
+-- HRAM simulation signals
+signal hr_d : unsigned(7 downto 0); -- Data/Address
+signal hr_rwds : std_logic; -- RW Data strobe
+signal hr_reset : std_logic; -- Active low RESET line to HyperRAM
+signal hr_clk_p : std_logic;
+signal hr2_d : unsigned(7 downto 0); -- Data/Address
+signal hr2_rwds : std_logic; -- RW Data strobe
+signal hr2_reset : std_logic; -- Active low RESET line to HyperRAM
+signal hr2_clk_p : std_logic;
+signal hr_cs0 : std_logic;
+signal hr_cs1 : std_logic;
+
+signal gbl_reset : std_logic;
+signal reset_counter : unsigned(15 downto 0) := x"0000";
+
+begin
+
+ cpu_data_in <= switch_data_out or rom_data_out or ram_data_out or hram_data_out;
+
+ fakehyper0: entity work.s27kl0641
+ generic map (
+ id => "$8000000",
+ tdevice_vcs => 5 ns,
+ timingmodel => "S27KL0641DABHI000"
+ )
+ port map (
+ DQ7 => hr_d(7),
+ DQ6 => hr_d(6),
+ DQ5 => hr_d(5),
+ DQ4 => hr_d(4),
+ DQ3 => hr_d(3),
+ DQ2 => hr_d(2),
+ DQ1 => hr_d(1),
+ DQ0 => hr_d(0),
+
+ CSNeg => hr_cs0,
+ CK => hr_clk_p,
+ RESETneg => hr_reset,
+ RWDS => hr_rwds
+ );
+
+ fakehyper1: entity work.s27kl0641
+ generic map (
+ id => "$8800000",
+ tdevice_vcs => 5 ns,
+ timingmodel => "S27KL0641DABHI000"
+ )
+ port map (
+ DQ7 => hr2_d(7),
+ DQ6 => hr2_d(6),
+ DQ5 => hr2_d(5),
+ DQ4 => hr2_d(4),
+ DQ3 => hr2_d(3),
+ DQ2 => hr2_d(2),
+ DQ1 => hr2_d(1),
+ DQ0 => hr2_d(0),
+
+ CSNeg => hr_cs1,
+ CK => hr2_clk_p,
+ RESETneg => hr2_reset,
+ RWDS => hr2_rwds
+ );
+
+ -- QNICE CPU
+ cpu : entity work.QNICE_CPU
+ port map (
+ CLK => SLOW_CLOCK,
+ RESET => gbl_reset,
+ WAIT_FOR_DATA => cpu_wait_for_data,
+ ADDR => cpu_addr,
+ DATA_IN => cpu_data_in,
+ DATA_OUT => cpu_data_out,
+ DATA_DIR => cpu_data_dir,
+ DATA_VALID => cpu_data_valid,
+ HALT => cpu_halt
+ );
+
+ -- ROM: up to 64kB consisting of up to 32.000 16 bit words
+ rom : entity work.BROM
+ generic map (
+ FILE_NAME => ROM_FILE
+ )
+ port map (
+ clk => SLOW_CLOCK,
+ ce => rom_enable,
+ address => cpu_addr(14 downto 0),
+ data => rom_data_out,
+ busy => rom_busy
+ );
+
+ -- RAM: up to 64kB consisting of up to 32.000 16 bit words
+ ram : entity work.BRAM
+ port map (
+ clk => SLOW_CLOCK,
+ ce => ram_enable,
+ address => cpu_addr(14 downto 0),
+ we => cpu_data_dir,
+ data_i => cpu_data_out,
+ data_o => ram_data_out,
+ busy => ram_busy
+ );
+
+ -- HyperRAM
+ HRAM : entity work.hyperram_ctl
+ port map (
+ clk => SLOW_CLOCK,
+ clk2x => CLK1x,
+ clk4x => CLK2x,
+ reset => gbl_reset,
+ hram_en => hram_en,
+ hram_we => hram_we,
+ hram_reg => hram_reg,
+ hram_cpu_ws => hram_cpu_ws,
+ data_in => cpu_data_out,
+ data_out => hram_data_out,
+ hr_d => hr_d,
+ hr_rwds => hr_rwds,
+ hr_reset => hr_reset,
+ hr_clk_p => hr_clk_p,
+ hr_cs0 => hr_cs0
+ );
+
+ -- memory mapped i/o controller
+ mmio_controller : entity work.mmio_mux
+ port map (
+ HW_RESET => gbl_reset,
+ CLK => SLOW_CLOCK,
+ addr => cpu_addr,
+ data_dir => cpu_data_dir,
+ data_valid => cpu_data_valid,
+ cpu_wait_for_data => cpu_wait_for_data,
+ cpu_halt => cpu_halt,
+ rom_enable => rom_enable,
+ rom_busy => rom_busy,
+ ram_enable => ram_enable,
+ ram_busy => ram_busy,
+ switch_reg_enable => switch_reg_enable,
+ hram_en => hram_en,
+ hram_we => hram_we,
+ hram_reg => hram_reg,
+ hram_cpu_ws => hram_cpu_ws
+ );
+
+ generate_clocks: process
+ begin
+ SLOW_CLOCK <= '0';
+ CLK1x <= '0';
+ CLK2x <= '0';
+
+ wait for 2.5 ns;
+ CLK2x <= '1';
+
+ wait for 2.5 ns;
+ CLK2x <= '0';
+ CLK1x <= '1';
+
+ wait for 2.5 ns;
+ CLK2x <= '1';
+
+ wait for 2.5 ns;
+ CLK2x <= '0';
+ CLK1x <= '0';
+ SLOW_CLOCK <= '1';
+
+ wait for 2.5 ns;
+ CLK2x <= '1';
+
+ wait for 2.5 ns;
+ CLK2x <= '0';
+ CLK1x <= '1';
+
+ wait for 2.5 ns;
+ CLK2x <= '1';
+
+ wait for 2.5 ns;
+ end process;
+
+ startup_reset_handler : process(CLK1x)
+ begin
+ if rising_edge(CLK1x) then
+ if reset_counter < x"3B1" then
+ reset_counter <= reset_counter + 1;
+ end if;
+ end if;
+ end process;
+
+ -- HRAM needs *very* long to initialize ("busy=1" at the beginning)
+ gbl_reset <= '1' when reset_counter < x"3B1" else '0';
+
+ -- handle the toggle switches
+ switch_data_out <= x"0000";
+
+end beh;
diff --git a/vhdl/hw/MEGA65/sync_reg.vhd b/vhdl/hw/MEGA65/sync_reg.vhd
new file mode 100644
index 00000000..2c52f79e
--- /dev/null
+++ b/vhdl/hw/MEGA65/sync_reg.vhd
@@ -0,0 +1,87 @@
+--------------------------------------------------------------------------------
+-- sync_reg.vhd --
+-- Synchronising register(s) for clock domain crossing. --
+--------------------------------------------------------------------------------
+-- (C) Copyright 2022 Adam Barnes --
+-- This file is part of The Tyto Project. The Tyto Project is free software: --
+-- you can redistribute it and/or modify it under the terms of the GNU Lesser --
+-- General Public License as published by the Free Software Foundation, --
+-- either version 3 of the License, or (at your option) any later version. --
+-- The Tyto Project is distributed in the hope that it will be useful, but --
+-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public --
+-- License for more details. You should have received a copy of the GNU --
+-- Lesser General Public License along with The Tyto Project. If not, see --
+-- https://www.gnu.org/licenses/. --
+--------------------------------------------------------------------------------
+
+library ieee;
+ use ieee.std_logic_1164.all;
+
+package sync_reg_pkg is
+
+ component sync_reg is
+ generic (
+ width : integer := 1;
+ depth : integer := 2;
+ rst_state : std_logic := '0'
+ );
+ port (
+ rst : in std_logic := '0';
+ clk : in std_logic;
+ d : in std_logic_vector(width-1 downto 0);
+ q : out std_logic_vector(width-1 downto 0)
+ );
+ end component sync_reg;
+
+end package sync_reg_pkg;
+
+-------------------------------------------------------------------------------
+
+library ieee;
+ use ieee.std_logic_1164.all;
+
+entity sync_reg is
+ generic (
+ width : integer := 1;
+ depth : integer := 2;
+ rst_state : std_logic := '0'
+ );
+ port (
+ rst : in std_logic := '0';
+ clk : in std_logic; -- destination clock
+ d : in std_logic_vector(width-1 downto 0); -- input
+ q : out std_logic_vector(width-1 downto 0) -- output
+ );
+end entity sync_reg;
+
+architecture structural of sync_reg is
+
+ subtype reg_level_t is std_logic_vector(width-1 downto 0);
+ type reg_t is array(0 to depth-1) of reg_level_t;
+
+ signal reg : reg_t;
+
+ attribute async_reg : string;
+ attribute async_reg of reg : signal is "TRUE";
+
+begin
+
+ MAIN: process (rst,clk) is
+ begin
+ if rst = '1' then
+ reg <= (others => (others => rst_state));
+ elsif rising_edge(clk) then
+ for i in 0 to depth-1 loop
+ if i = 0 then
+ reg(i) <= d;
+ else
+ reg(i) <= reg(i-1);
+ end if;
+ end loop;
+ end if;
+ end process MAIN;
+
+ q <= reg(depth-1);
+
+end architecture structural;
diff --git a/vhdl/hw/MEGA65/types_pkg.vhd b/vhdl/hw/MEGA65/types_pkg.vhd
new file mode 100644
index 00000000..5c6bcfc7
--- /dev/null
+++ b/vhdl/hw/MEGA65/types_pkg.vhd
@@ -0,0 +1,32 @@
+--------------------------------------------------------------------------------
+-- types_pkg.vhd --
+-- Common types package. --
+--------------------------------------------------------------------------------
+-- (C) Copyright 2020 Adam Barnes --
+-- This file is part of The Tyto Project. The Tyto Project is free software: --
+-- you can redistribute it and/or modify it under the terms of the GNU Lesser --
+-- General Public License as published by the Free Software Foundation, --
+-- either version 3 of the License, or (at your option) any later version. --
+-- The Tyto Project is distributed in the hope that it will be useful, but --
+-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public --
+-- License for more details. You should have received a copy of the GNU --
+-- Lesser General Public License along with The Tyto Project. If not, see --
+-- https://www.gnu.org/licenses/. --
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+
+package types_pkg is
+
+ type slv_31_0_t is array(natural range <>) of std_logic_vector(31 downto 0);
+ type slv_9_0_t is array(natural range <>) of std_logic_vector(9 downto 0);
+ type slv_7_0_t is array(natural range <>) of std_logic_vector(7 downto 0);
+ type slv_3_0_t is array(natural range <>) of std_logic_vector(3 downto 0);
+ type slv_1_0_t is array(natural range <>) of std_logic_vector(1 downto 0);
+
+ type slv_7_0_2d_t is array(natural range <>,natural range <>) of std_logic_vector(7 downto 0);
+ type slv_3_0_2d_t is array(natural range <>,natural range <>) of std_logic_vector(3 downto 0);
+
+end package types_pkg;
diff --git a/vhdl/hw/MEGA65/vga_to_hdmi.vhd b/vhdl/hw/MEGA65/vga_to_hdmi.vhd
new file mode 100644
index 00000000..7d345f5e
--- /dev/null
+++ b/vhdl/hw/MEGA65/vga_to_hdmi.vhd
@@ -0,0 +1,944 @@
+--------------------------------------------------------------------------------
+-- vga_to_hdmi.vhd --
+-- VGA to HDMI converter IP core; also injects PCM stereo audio. --
+--------------------------------------------------------------------------------
+-- (C) Copyright 2022 Adam Barnes --
+-- This file is part of The Tyto Project. The Tyto Project is free software: --
+-- you can redistribute it and/or modify it under the terms of the GNU Lesser --
+-- General Public License as published by the Free Software Foundation, --
+-- either version 3 of the License, or (at your option) any later version. --
+-- The Tyto Project is distributed in the hope that it will be useful, but --
+-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public --
+-- License for more details. You should have received a copy of the GNU --
+-- Lesser General Public License along with The Tyto Project. If not, see --
+-- https://www.gnu.org/licenses/. --
+--------------------------------------------------------------------------------
+
+library ieee;
+ use ieee.std_logic_1164.all;
+ use ieee.numeric_std.all;
+
+library work;
+ use work.types_pkg.all;
+ use work.sync_reg_pkg.all;
+ use work.hdmi_tx_encoder_pkg.all;
+
+entity vga_to_hdmi is
+ port (
+
+ -- Select which of the two audio clocks above to use
+ select_44100 : in std_logic;
+
+ dvi : in std_logic; -- DVI mode disables all HDMI enhancements e.g. audio
+ vic : in std_logic_vector(7 downto 0); -- CEA/CTA VIC
+ aspect : in std_logic_vector(1 downto 0); -- for aspect ratio signalling in AVI InfoFrames
+ pix_rep : in std_logic; -- signals pixel repetition (SD interlaced modes)
+ vs_pol : in std_logic; -- vertical sync output polarity } 1 = active high
+ hs_pol : in std_logic; -- horizontal sync output polarity }
+
+ vga_rst : in std_logic; -- reset
+ vga_clk : in std_logic; -- pixel clock
+ vga_vs : in std_logic; -- vertical sync } active high
+ vga_hs : in std_logic; -- horizontal sync }
+ vga_de : in std_logic; -- data (pixel) enable
+ vga_r : in std_logic_vector(7 downto 0); -- pixel data, red channel }
+ vga_g : in std_logic_vector(7 downto 0); -- pixel data, green channel } 0..255
+ vga_b : in std_logic_vector(7 downto 0); -- pixel data, blue channel }
+
+ pcm_rst : in std_logic; -- audio reset
+ pcm_clk : in std_logic; -- audio clock } combined = fs
+ pcm_clken : in std_logic; -- audio clock enable } (audio sample rate)
+ pcm_l : in std_logic_vector(15 downto 0); -- left channel } audio
+ pcm_r : in std_logic_vector(15 downto 0); -- right channel } sample
+ pcm_acr : in std_logic; -- HDMI ACR packet strobe (frequency = 128fs/N e.g. 1kHz)
+ pcm_n : in std_logic_vector(19 downto 0); -- HDMI ACR N value
+ pcm_cts : in std_logic_vector(19 downto 0); -- HDMI ACR CTS value
+
+ tmds : out slv_9_0_t(0 to 2) -- parallel TMDS symbol stream x 3 channels
+
+ );
+end entity vga_to_hdmi;
+
+architecture synth of vga_to_hdmi is
+
+ constant SUBPACKETS : integer := 4; -- per packet
+ constant PACKET_TYPES : integer := 4; -- types of data packet supported in this design
+
+ constant CTL_NULL : std_logic_vector(3 downto 0) := "0000";
+ constant CTL_PRE_VIDEO : std_logic_vector(3 downto 0) := "0001"; -- video period preamble
+ constant CTL_PRE_DATA : std_logic_vector(3 downto 0) := "0101"; -- data period preamble
+
+ ----------------------------------------------------------------------
+ -- audio/ACR related
+
+ signal pcm_n_s : std_logic_vector(19 downto 0); -- pcm_n } synchronised
+ signal pcm_cts_s : std_logic_vector(19 downto 0); -- pcm_cts }
+ signal pcm_cs_v : std_logic_vector(0 to 39); -- IEC60958 channel status as a vector
+ signal pcm_count : integer range 0 to SUBPACKETS-1; -- used to assemble 4x samples into sample packet
+
+ signal iec_req : std_logic; -- PCM to IEC sample request
+ signal iec_count : integer range 0 to 191; -- IEC60958 frame #
+ signal iec_sync : std_logic; -- IEC60958 b preamble (sync)
+ signal iec_l : std_logic_vector(23 downto 0); -- IEC60958 left channel sample
+ signal iec_lv : std_logic; -- IEC60958 left channel status
+ signal iec_lu : std_logic; -- IEC60958 left user data
+ signal iec_lc : std_logic; -- IEC60958 left channel status
+ signal iec_lp : std_logic; -- IEC60958 left channel status
+ signal iec_r : std_logic_vector(23 downto 0); -- IEC60958 right channel sample
+ signal iec_rv : std_logic; -- IEC60958 right channel status
+ signal iec_ru : std_logic; -- IEC60958 right user data
+ signal iec_rc : std_logic; -- IEC60958 right channel status
+ signal iec_rp : std_logic; -- IEC60958 right channel status
+ signal iec_ack : std_logic; -- }
+
+ signal iec_req_s : std_logic_vector(0 to 4); -- } clock domain crossing logic
+ signal pcm_acr_s : std_logic_vector(0 to 2); -- }
+
+ signal vga_iec_en : std_logic; -- enable for the following...
+ signal vga_iec_sync : std_logic; -- iec_sync } synchronised to vga_clk
+ signal vga_iec_l : std_logic_vector(23 downto 0); -- iec_l }
+ signal vga_iec_lv : std_logic; -- iec_lv }
+ signal vga_iec_lu : std_logic; -- iec_lu }
+ signal vga_iec_lc : std_logic; -- iec_lc }
+ signal vga_iec_lp : std_logic; -- iec_lp }
+ signal vga_iec_r : std_logic_vector(23 downto 0); -- iec_r }
+ signal vga_iec_rv : std_logic; -- iec_rv }
+ signal vga_iec_ru : std_logic; -- iec_ru }
+ signal vga_iec_rc : std_logic; -- iec_rc }
+ signal vga_iec_rp : std_logic; -- iec_rp }
+ signal vga_iec_data : std_logic_vector(56 downto 0); -- bus to carry above signals
+ signal vga_acr : std_logic; -- ACR enable pulse, synchronous to vga_clk
+
+ ----------------------------------------------------------------------
+ -- video input related
+
+ signal dvi_s : std_logic; -- dvi } synchronised
+ signal vic_s : std_logic_vector(7 downto 0); -- vic }
+ signal aspect_s : std_logic_vector(1 downto 0); -- aspect }
+ signal pix_rep_s : std_logic; -- pix_rep }
+ signal vs_pol_s : std_logic; -- vs_pol }
+ signal hs_pol_s : std_logic; -- hs_pol }
+
+ signal vga_vs_p : std_logic; -- vga_vs } polarity adjusted
+ signal vga_hs_p : std_logic; -- vga_hs }
+
+ signal vga_vs_1 : std_logic; -- vga_vs, delayed by 1 clock
+ signal vga_hs_1 : std_logic; -- vga_hs, delayed by 1 clock
+ signal vga_de_1 : std_logic; -- vga_de, delayed by 1 clock
+
+ constant buf_size : integer := 128;
+ type buf_t is array(0 to buf_size-1) of std_logic_vector(26 downto 0);
+
+ signal buf : buf_t; -- video input buffer (circular)
+ signal buf_addr : integer range 0 to buf_size-1; -- video input buffer write/read address
+ signal buf_valid : boolean; -- indicates buffer has been filled once
+ signal buf_vs : std_logic; -- buffered VGA vertical sync
+ signal buf_hs : std_logic; -- buffered VGA horizontal sync
+ signal buf_de : std_logic; -- buffered VGA data (pixel) enable
+ signal buf_p : slv_7_0_t(0 to 2); -- buffered VGA pixel data, 3 channels
+ signal blank_count : integer range 0 to buf_size; -- video blank count
+
+ ----------------------------------------------------------------------
+ -- encoding related
+
+ type u8 is array(natural range <>) of unsigned(7 downto 0);
+ type hb_array_t is array(0 to PACKET_TYPES-1) of u8(0 to 2);
+ type pb_array_t is array(0 to PACKET_TYPES-1) of u8(0 to 27);
+ type sb_array_t is array(0 to 3) of u8(0 to 6);
+ type period_t is (
+ CONTROL,
+ VIDEO_PRE,
+ VIDEO_GB,
+ VIDEO,
+ DATA_PRE,
+ DATA_GB_LEADING,
+ DATA_ISLAND,
+ DATA_GB_TRAILING
+ );
+
+ signal data_req : std_logic_vector(0 to PACKET_TYPES-1); -- } data packet handshaking
+ signal data_ack : std_logic_vector(0 to PACKET_TYPES-1); -- }
+
+ signal hb_a : u8(0 to 2); -- header bytes of audio sample packet in progress
+ signal pb_a : u8(0 to 27); -- packet bytes of audio sample packet in progress
+ signal hb : hb_array_t; -- header bytes for packet types 0..3
+ signal pb : pb_array_t; -- packet bytes for packet types 0..3
+
+ -- HDMI encoding pipeline stage 1...
+ signal s1_period : period_t; -- period type
+ signal s1_pcount : unsigned(4 downto 0); -- counts time spent in period up to a max of 31 (also data island character #)
+ signal s1_dcount : unsigned(4 downto 0); -- counts consecutive data packets (max allowed is 18)
+ signal s1_hb : u8(0 to 2); -- current header
+ signal s1_sb : sb_array_t; -- current set of 4 subpackets
+ signal s1_vs : std_logic; -- vertical sync (pipelined from buffered input)
+ signal s1_hs : std_logic; -- horizontal sync (pipelined from buffered input)
+ signal s1_de : std_logic; -- data (pixel) enable (pipelined from buffered from input)
+ signal s1_p : slv_7_0_t(0 to 2); -- pixel data (3 channels) (pipelined from buffered input)
+ signal s1_enc : std_logic_vector(1 downto 0); -- encoding type required
+ signal s1_ctl : std_logic_vector(3 downto 0); -- CTL bits (indicate preamble type)
+
+ -- HDMI encoding pipeline stage 2...
+ signal s2_data : std_logic; -- data period active
+ signal s2_pcount : unsigned(s1_pcount'range); -- data island character #=
+ signal s2_bch4 : std_logic; -- BCH block 4 bit
+ signal s2_bch_e : std_logic_vector(3 downto 0); -- BCH blocks 0-3 even bit
+ signal s2_bch_o : std_logic_vector(3 downto 0); -- BCH blocks 0-3 odd bit
+ signal s2_vs : std_logic; -- vertical sync (pipelined from previous stage)
+ signal s2_hs : std_logic; -- horizontal sync (pipelined from previous stage)
+ signal s2_de : std_logic; -- data (pixel) enable (pipelined from previous stage)
+ signal s2_p : slv_7_0_t(0 to 2); -- pixel data (3 channels) (pipelined from previous stage)
+ signal s2_enc : std_logic_vector(1 downto 0); -- encoding type (pipelined from previous stage)
+ signal s2_ctl : std_logic_vector(3 downto 0); -- CTL bits (pipelined from previous stage)
+
+ -- HDMI encoding pipeline stage 3...
+ signal s3_data : std_logic; -- data period active
+ signal s3_pcount : unsigned(s1_pcount'range); -- data island character #
+ signal s3_bch4 : std_logic; -- BCH block 4 bit
+ signal s3_bch_e : std_logic_vector(s2_bch_e'range); -- BCH blocks 0-3 even bit
+ signal s3_bch_o : std_logic_vector(s2_bch_o'range); -- BCH blocks 0-3 odd bit
+ signal s3_bch_ecc : slv_7_0_t(0 to SUBPACKETS); -- ECC values for header (0) and 4 subpackets (1..4)
+ signal s3_vs : std_logic; -- vertical sync (pipelined from previous stage)
+ signal s3_hs : std_logic; -- horizontal sync (pipelined from previous stage)
+ signal s3_de : std_logic; -- data (pixel) enable (pipelined from previous stage)
+ signal s3_p : slv_7_0_t(0 to 2); -- pixel data (3 channels) (pipelined from previous stage)
+ signal s3_enc : std_logic_vector(1 downto 0); -- encoding type (pipelined from previous stage)
+ signal s3_ctl : std_logic_vector(3 downto 0); -- CTL bits (pipelined from previous stage)
+
+ -- HDMI encoding pipeline stage 4...
+ signal s4_vs : std_logic; -- vertical sync (pipelined from previous stage)
+ signal s4_hs : std_logic; -- horizontal sync (pipelined from previous stage)
+ signal s4_de : std_logic; -- data (pixel) enable (pipelined from previous stage)
+ signal s4_p : slv_7_0_t(0 to 2); -- pixel data (3 channels) (pipelined from previous stage)
+ signal s4_enc : std_logic_vector(1 downto 0); -- encoding type (pipelined from previous stage)
+ signal s4_ctl : std_logic_vector(3 downto 0); -- CTL bits (pipelined from previous stage)
+ signal s4_c : slv_1_0_t(0 to 2); -- C input to TMDS encoder x 3 channels
+ signal s4_d : slv_3_0_t(0 to 2); -- aux data input to TMDS encoder x 3 channels
+
+ ----------------------------------------------------------------------
+ -- constant packet content
+
+ -- packet type 0: audio clock sample packet
+ constant hb_0 : u8(0 to 2) := (x"02", x"0F", x"00");
+
+ -- packet type 1: audio clock regeneration packet
+ constant hb_1 : u8(0 to 2) := (x"01", x"00", x"00");
+
+ -- packet type 2: audio infoframe packet
+ constant hb_2 : u8(0 to 2) := (x"84", x"01", x"0A");
+ constant pb_2 : u8(0 to 27) :=
+ (
+ 0 => x"70", -- checksum 84+01+0A+01+CKS = 00
+ 1 => x"01", -- 00000001 CT(3:0),RSVD,CC(2:0)
+ 2 => x"00", -- 00000000 F(27:25),SF(2:0),SS(1:0)
+ 3 => x"00", -- 00000000 F(37:35),CXT(4:0)
+ 4 => x"00", -- 00000000 CA(7:0)
+ 5 => x"00", -- 00000001 DM_INH,LSV(3:0),F(52),LFEPBL(1:0)
+ 6 => x"00", -- 00000000 F(67:60)
+ 7 => x"00", -- 00000000 F(77:70)
+ 8 => x"00", -- 00000000 F(87:80)
+ 9 => x"00", -- 00000000 F(97:90)
+ 10 => x"00", -- 00000000 F(107:100)
+ others => x"00" -- zero
+ );
+
+ -- type 3: AVI infoframe packet
+ constant hb_3 : u8(0 to 2) := (x"82", x"02", x"0D");
+ constant pb_3 : u8(0 to 27) :=
+ (
+ 0 => x"00", -- *NOT CONSTANT* checksum
+ 1 => x"02", -- RSVD,Y(1:0),A0,B(1:0),S(1:0)
+ 2 => x"00", -- *PART CONSTANT* C(1:0),M(1:0),R(3:0)
+ 3 => x"80", -- ITC,EC(2:0),Q(1:0),SC(1:0)
+ 4 => x"00", -- *NOT CONSTANT* VIC
+ 5 => x"30", -- *PART CONSTANT* YQ(1:0),CN(1:0),PR(3:0)
+ others => x"00" -- zero
+ );
+
+ function sum (data : u8) return unsigned is
+ variable r : unsigned(7 downto 0);
+ begin
+ r := x"00";
+ for i in 0 to data'length-1 loop
+ r := r + data(i);
+ end loop;
+ return r;
+ end function sum;
+
+ constant sum_3 : unsigned(7 downto 0) := sum(hb_3 & pb_3);
+
+----------------------------------------------------------------------
+
+begin
+
+ -- add IEC60958 channel status to PCM stream
+
+ pcm_cs_v(0) <= '0'; -- consumer use
+ pcm_cs_v(1) <= '0'; -- PCM samples
+ pcm_cs_v(2) <= '1'; -- no copyright
+ pcm_cs_v(3 to 5) <= "000"; -- 2 channels without pre-emphasis
+ pcm_cs_v(6 to 7) <= "00"; -- channel status mode 0
+ pcm_cs_v(8 to 15) <= "01000001"; -- category code
+ pcm_cs_v(16 to 19) <= "0000"; -- source - do not take into account
+ pcm_cs_v(20 to 23) <= "0000"; -- channel number - DYNAMIC (1/left or 2/right)
+ pcm_cs_v(28 to 29) <= "00"; -- clock accuracy level 2
+ pcm_cs_v(30) <= '0'; -- reserved
+ pcm_cs_v(31) <= '0'; -- reserved
+ pcm_cs_v(32) <= '0'; -- max sample word length is 20 bits
+ pcm_cs_v(33 to 35) <= "100"; -- sample word length is 16 bits
+ pcm_cs_v(36 to 39) <= "0000"; -- original sample frequency not indicated
+
+ PCM: process (pcm_rst, pcm_clk) is
+ variable cs, cs_l, cs_r : std_logic;
+ function xor_v (v : std_logic_vector) return std_logic is
+ variable i : integer;
+ variable r : std_logic;
+ begin
+ r := '0';
+ for i in 0 to v'length-1 loop
+ r := r xor v(i);
+ end loop;
+ return r;
+ end function xor_v;
+
+ begin
+ if pcm_rst = '1' then
+ -- iec_req <= '0';
+ iec_count <= 0;
+ iec_sync <= '0';
+ iec_l <= (others => '0');
+ iec_lv <= '0';
+ iec_lu <= '0';
+ iec_lc <= '0';
+ iec_lp <= '0';
+ iec_r <= (others => '0');
+ iec_rv <= '0';
+ iec_ru <= '0';
+ iec_rc <= '0';
+ iec_rp <= '0';
+ elsif rising_edge(pcm_clk) then
+
+ -- Inform sink of the correct audio sample rate
+ if select_44100 = '0' then
+ pcm_cs_v(24 to 27) <= "0100"; -- 48KHz
+ else
+ pcm_cs_v(24 to 27) <= "0000"; -- 44.1KHz
+ end if;
+
+ if pcm_clken = '1' then
+ if iec_count < 40 then
+ cs := pcm_cs_v(iec_count);
+ else
+ cs := '0';
+ end if;
+ cs_l := cs;
+ cs_r := cs;
+ if iec_count = 20 then
+ cs_l := '1'; -- set channel number for left channel
+ elsif iec_count = 21 then
+ cs_r := '1'; -- set channel number for right channel
+ end if;
+ iec_sync <= '0';
+ if iec_count = 0 then
+ iec_sync <= '1';
+ end if;
+ iec_l <= pcm_l & x"00";
+ iec_lv <= '0';
+ iec_lu <= '0';
+ iec_lc <= cs_l;
+ iec_lp <= xor_v(pcm_l & cs_l);
+ iec_r <= pcm_r & x"00";
+ iec_rv <= '0';
+ iec_ru <= '0';
+ iec_rc <= cs_r;
+ iec_rp <= xor_v(pcm_r & cs_r);
+ if iec_count = 191 then
+ iec_count <= 0;
+ else
+ iec_count <= iec_count+1;
+ end if;
+ -- iec_req <= '1';
+ -- elsif iec_ack = '1' then
+ -- iec_req <= '0';
+ end if;
+ end if;
+ end process PCM;
+
+ P_IEC_REQ: process (all) is
+ begin
+ if pcm_rst = '1' or iec_ack = '1' then
+ iec_req <= '0';
+ elsif rising_edge(pcm_clk) and pcm_clken = '1' then
+ iec_req <= '1';
+ end if;
+ end process P_IEC_REQ;
+
+ -- clock domain crossing
+
+ SYNC1: component sync_reg
+ generic map (
+ WIDTH => 8,
+ DEPTH => 1
+ )
+ port map (
+ clk => vga_clk,
+ rst => vga_rst,
+ d(0) => pcm_acr,
+ d(1) => pcm_acr_s(0),
+ d(2) => pcm_acr_s(1),
+ d(3) => iec_req,
+ d(4) => iec_req_s(0),
+ d(5) => iec_req_s(1),
+ d(6) => iec_req_s(2),
+ d(7) => iec_req_s(3),
+ q(0) => pcm_acr_s(0),
+ q(1) => pcm_acr_s(1),
+ q(2) => pcm_acr_s(2),
+ q(3) => iec_req_s(0),
+ q(4) => iec_req_s(1),
+ q(5) => iec_req_s(2),
+ q(6) => iec_req_s(3),
+ q(7) => iec_req_s(4)
+ );
+
+ vga_acr <= pcm_acr_s(1) and not pcm_acr_s(2);
+ vga_iec_en <= iec_req_s(3) and not iec_req_s(4);
+ iec_ack <= iec_req_s(4);
+
+ -- SYNC2: component sync_reg
+ -- generic map (
+ -- WIDTH => 1,
+ -- DEPTH => 2
+ -- )
+ -- port map (
+ -- clk => pcm_clk,
+ -- d(0) => iec_req_s(2),
+ -- q(0) => iec_ack
+ -- );
+
+ SYNC3: component sync_reg
+ generic map (
+ WIDTH => 57,
+ DEPTH => 2
+ )
+ port map (
+ clk => vga_clk,
+ rst => vga_rst,
+ d(56) => iec_sync,
+ d(55) => iec_rp,
+ d(54) => iec_rc,
+ d(53) => iec_ru,
+ d(52) => iec_rv,
+ d(51 downto 28) => iec_r,
+ d(27) => iec_lp,
+ d(26) => iec_lc,
+ d(25) => iec_lu,
+ d(24) => iec_lv,
+ d(23 downto 0) => iec_l,
+ q(56) => vga_iec_sync,
+ q(55) => vga_iec_rp,
+ q(54) => vga_iec_rc,
+ q(53) => vga_iec_ru,
+ q(52) => vga_iec_rv,
+ q(51 downto 28) => vga_iec_r,
+ q(27) => vga_iec_lp,
+ q(26) => vga_iec_lc,
+ q(25) => vga_iec_lu,
+ q(24) => vga_iec_lv,
+ q(23 downto 0) => vga_iec_l
+ );
+
+ SYNC4 : component sync_reg
+ generic map (
+ WIDTH => 54,
+ DEPTH => 2
+ )
+ port map (
+ clk => vga_clk,
+ rst => vga_rst,
+ d(53 downto 34) => pcm_n,
+ d(33 downto 14) => pcm_cts,
+ d(13) => dvi,
+ d(12 downto 5) => vic,
+ d(4 downto 3) => aspect,
+ d(2) => pix_rep,
+ d(1) => vs_pol,
+ d(0) => hs_pol,
+ q(53 downto 34) => pcm_n_s,
+ q(33 downto 14) => pcm_cts_s,
+ q(13) => dvi_s,
+ q(12 downto 5) => vic_s,
+ q(4 downto 3) => aspect_s,
+ q(2) => pix_rep_s,
+ q(1) => vs_pol_s,
+ q(0) => hs_pol_s
+ );
+
+ -- inject preambles, guardbands and data packets
+
+ vga_vs_p <= vga_vs xnor vs_pol_s;
+ vga_hs_p <= vga_hs xnor hs_pol_s;
+
+ VGA: process (vga_rst, vga_clk) is
+
+ variable buf_rdata : std_logic_vector(26 downto 0);
+ variable p : integer range 0 to PACKET_TYPES-1;
+ variable s1_hb_byte : integer range 0 to 3;
+ variable s1_hb_bit : integer range 0 to 7;
+ variable s1_sb_byte : integer range 0 to 7;
+ variable s1_sb_2bit : integer range 0 to 3;
+
+ -- BCH ECC functions (see hdmi_bch_ecc.py)
+ function bch_ecc_1 ( -- 1 bit per clock
+ q : std_logic_vector(7 downto 0);
+ d : std_logic
+ ) return std_logic_vector is
+ variable r : std_logic_vector(7 downto 0);
+ begin
+ r(0) := d xor q(0) xor q(1);
+ r(1) := d xor q(0) xor q(2);
+ r(2) := q(3);
+ r(3) := q(4);
+ r(4) := q(5);
+ r(5) := q(6);
+ r(6) := q(7);
+ r(7) := d xor q(0);
+ return r;
+ end function bch_ecc_1;
+
+ function bch_ecc_2 ( -- 2 bits per clock
+ q : std_logic_vector(7 downto 0);
+ d : std_logic_vector(1 downto 0)
+ ) return std_logic_vector is
+ variable r : std_logic_vector(7 downto 0);
+ begin
+ r(0) := d(1) xor q(1) xor q(2);
+ r(1) := d(0) xor d(1) xor q(0) xor q(1) xor q(3);
+ r(2) := q(4);
+ r(3) := q(5);
+ r(4) := q(6);
+ r(5) := q(7);
+ r(6) := d(0) xor q(0);
+ r(7) := d(0) xor d(1) xor q(0) xor q(1);
+ return r;
+ end function bch_ecc_2;
+
+ begin
+
+ if vga_rst = '1' then
+
+ vga_vs_1 <= '0';
+ vga_hs_1 <= '0';
+ vga_de_1 <= '0';
+ buf_valid <= false;
+ buf_addr <= 0;
+ buf_vs <= '0';
+ buf_hs <= '0';
+ buf_de <= '0';
+ buf_p <= (others => (others => '0'));
+ blank_count <= 0;
+ data_req <= (others => '0');
+ data_ack <= (others => '0');
+ pcm_count <= 0;
+ hb_a <= (others => (others => '0'));
+ pb_a <= (others => (others => '0'));
+ hb <= (others => (others => (others => '0')));
+ pb <= (others => (others => (others => '0')));
+ s1_period <= CONTROL;
+ s1_pcount <= (others => '0');
+ s1_dcount <= (others => '0');
+ s1_hb <= (others => (others => '0'));
+ s1_sb <= (others => (others => (others => '0')));
+ s1_vs <= '0';
+ s1_hs <= '0';
+ s1_de <= '0';
+ s1_p <= (others => (others => '0'));
+ s1_enc <= ENC_DVI;
+ s1_ctl <= CTL_NULL;
+ s2_data <= '0';
+ s2_pcount <= (others => '0');
+ s2_bch4 <= '0';
+ s2_bch_e <= (others => '0');
+ s2_bch_o <= (others => '0');
+ s2_vs <= '0';
+ s2_hs <= '0';
+ s2_de <= '0';
+ s2_p <= (others => (others => '0'));
+ s2_enc <= ENC_DVI;
+ s2_ctl <= CTL_NULL;
+ s3_data <= '0';
+ s3_pcount <= (others => '0');
+ s3_bch4 <= '0';
+ s3_bch_e <= (others => '0');
+ s3_bch_o <= (others => '0');
+ s3_bch_ecc <= (others => (others => '0'));
+ s3_vs <= '0';
+ s3_hs <= '0';
+ s3_de <= '0';
+ s3_p <= (others => (others => '0'));
+ s3_enc <= ENC_DVI;
+ s3_ctl <= CTL_NULL;
+ s4_vs <= '0';
+ s4_hs <= '0';
+ s4_de <= '0';
+ s4_p <= (others => (others => '0'));
+ s4_enc <= ENC_DVI;
+ s4_ctl <= CTL_NULL;
+ s4_d <= (others => (others => '0'));
+
+ elsif rising_edge(vga_clk) then
+
+ vga_vs_1 <= vga_vs_p;
+ vga_hs_1 <= vga_hs_p;
+ vga_de_1 <= vga_de;
+
+ -- video input buffer for blank counting
+
+ buf_rdata := buf(buf_addr); -- read before write
+ buf(buf_addr) <= vga_vs_p & vga_hs_p & vga_de & vga_b & vga_g & vga_r;
+ if buf_addr = buf_size-1 then
+ buf_addr <= 0;
+ buf_valid <= true;
+ else
+ buf_addr <= buf_addr+1;
+ end if;
+ if buf_valid = true then
+ buf_vs <= buf_rdata(26);
+ buf_hs <= buf_rdata(25);
+ buf_de <= buf_rdata(24);
+ buf_p(0) <= buf_rdata(23 downto 16);
+ buf_p(1) <= buf_rdata(15 downto 8);
+ buf_p(2) <= buf_rdata(7 downto 0);
+ else
+ buf_vs <= '0';
+ buf_hs <= '0';
+ buf_de <= '0';
+ buf_p <= (others => (others => '0'));
+ end if;
+ if buf_valid = true then
+ if vga_de_1 = '0' and buf_de = '1' then
+ blank_count <= blank_count+1;
+ elsif vga_de_1 = '1' and buf_de = '0' then
+ blank_count <= blank_count-1;
+ end if;
+ else
+ if vga_de_1 = '0' then
+ blank_count <= blank_count+1;
+ end if;
+ end if;
+
+ -- packet handshaking and contents
+
+ hb_a(0 to 1) <= hb_0(0 to 1);
+ hb_a(2)(3 downto 0) <= hb_0(2)(3 downto 0);
+ if vga_iec_en = '1' then
+ hb_a(2)(pcm_count+4) <= vga_iec_sync;
+ pb_a((pcm_count*7)+0) <= unsigned(vga_iec_l(7 downto 0));
+ pb_a((pcm_count*7)+1) <= unsigned(vga_iec_l(15 downto 8));
+ pb_a((pcm_count*7)+2) <= unsigned(vga_iec_l(23 downto 16));
+ pb_a((pcm_count*7)+3) <= unsigned(vga_iec_r(7 downto 0));
+ pb_a((pcm_count*7)+4) <= unsigned(vga_iec_r(15 downto 8));
+ pb_a((pcm_count*7)+5) <= unsigned(vga_iec_r(23 downto 16));
+ pb_a((pcm_count*7)+6) <=
+ vga_iec_rp & vga_iec_rc & vga_iec_ru & vga_iec_rv &
+ vga_iec_lp & vga_iec_lc & vga_iec_lu & vga_iec_lv;
+ pcm_count <= (pcm_count + 1) mod 4;
+ if pcm_count = 0 then
+ data_req(0) <= '1';
+ hb(0) <= hb_a;
+ pb(0) <= pb_a;
+ end if;
+ end if;
+
+ if vga_acr = '1' then
+ data_req(1) <= '1';
+ end if;
+ hb(1) <= hb_1;
+ pb(1) <= (others => x"00");
+ for i in 0 to 3 loop
+ pb(1)((i*7)+1) <= x"0" & unsigned(pcm_cts_s(19 downto 16));
+ pb(1)((i*7)+2) <= unsigned(pcm_cts_s(15 downto 8));
+ pb(1)((i*7)+3) <= unsigned(pcm_cts_s(7 downto 0));
+ pb(1)((i*7)+4) <= x"0" & unsigned(pcm_n_s(19 downto 16));
+ pb(1)((i*7)+5) <= unsigned(pcm_n_s(15 downto 8));
+ pb(1)((i*7)+6) <= unsigned(pcm_n_s(7 downto 0));
+ end loop;
+
+ if vga_vs_p = '1' and vga_vs_1 = '0' then -- once per field
+ data_req(2) <= '1';
+ end if;
+ hb(2) <= hb_2;
+ pb(2) <= pb_2;
+
+ if vga_vs_p = '1' and vga_vs_1 = '0' and vga_hs_p /= vga_hs_1 then -- once per frame
+ data_req(3) <= '1';
+ end if;
+ hb(3) <= hb_3;
+ pb(3)(0 to 5) <= pb_3(0 to 5);
+ pb(3)(0) <= -- checksum
+ 1 + not (
+ sum_3 +
+ pb(3)(2) +
+ pb(3)(4) +
+ pb(3)(5)(3 downto 0)
+ );
+ pb(3)(2)(5 downto 4) <= unsigned(aspect_s);
+ -- pb(3)(2)(3) <= '1';
+ -- pb(3)(2)(1 downto 0) <= unsigned(aspect_s);
+ pb(3)(4) <= unsigned(vic_s);
+ pb(3)(5)(0) <= pix_rep_s;
+ pb(3)(6 to 27) <= pb_3(6 to 27);
+
+ for i in 0 to PACKET_TYPES-1 loop
+ if data_ack(i) = '1' then
+ data_req(i) <= '0';
+ end if;
+ if data_req(i) = '0' then
+ data_ack(i) <= '0';
+ end if;
+ end loop;
+
+ -- HDMI encoding pipeline stage 1
+ -- NOTE: we do not currently meet the requirement for
+ -- extended control periods by design...
+
+ if s1_pcount /= "11111" then
+ s1_pcount <= s1_pcount+1;
+ end if;
+
+ case s1_period is
+
+ when CONTROL =>
+ if buf_de = '0' then
+ if vga_de = '1' and blank_count = 10 then -- counting down to video
+ s1_period <= VIDEO_PRE; s1_enc <= ENC_DVI; s1_ctl <= CTL_PRE_VIDEO;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ elsif unsigned(s1_pcount) >= 11 and blank_count >= 66 then
+ -- we have satisfied 12 clock minimum for a control period;
+ -- to safely insert a data packet we need time for the following:
+ -- clocks period
+ -- 8 data preamble
+ -- 2 data guardband
+ -- 32 single data packet
+ -- 2 data guardband
+ -- 12 minimum control period
+ -- 8 video preamble (can be included in above?!)
+ -- 2 video guardband
+ -- total: 66
+ if data_req /= "0000" then
+ s1_period <= DATA_PRE; s1_enc <= ENC_DVI; s1_ctl <= CTL_PRE_DATA;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ for i in 0 to PACKET_TYPES-1 loop -- prioritize
+ p := i;
+ exit when data_req(p) = '1';
+ end loop;
+ data_ack(p) <= '1';
+ s1_hb <= hb(p);
+ s1_sb(0) <= pb(p)(0 to 6);
+ s1_sb(1) <= pb(p)(7 to 13);
+ s1_sb(2) <= pb(p)(14 to 20);
+ s1_sb(3) <= pb(p)(21 to 27);
+ end if;
+ end if;
+ end if;
+
+ when VIDEO_PRE =>
+ if s1_pcount(2 downto 0) = "111" then
+ s1_period <= VIDEO_GB; s1_enc <= ENC_GB_VIDEO; s1_ctl <= CTL_NULL;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ end if;
+
+ when VIDEO_GB =>
+ if s1_pcount(0) = '1' then
+ s1_period <= VIDEO; s1_enc <= ENC_DVI; s1_ctl <= CTL_NULL;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ end if;
+
+ when VIDEO =>
+ if buf_de = '0' then
+ s1_period <= CONTROL; s1_enc <= ENC_DVI; s1_ctl <= CTL_NULL;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ end if;
+
+ when DATA_PRE =>
+ if s1_pcount(2 downto 0) = "111" then
+ s1_period <= DATA_GB_LEADING; s1_enc <= ENC_GB_DATA; s1_ctl <= CTL_NULL;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ end if;
+
+ when DATA_GB_LEADING =>
+ if s1_pcount(0) = '1' then
+ s1_period <= DATA_ISLAND; s1_enc <= ENC_DATA; s1_ctl <= CTL_NULL;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ end if;
+
+ when DATA_ISLAND =>
+ if (s1_pcount = "11111") then -- last clock of this packet
+ if (blank_count >= 56) -- there is enough blanking time for another packet
+ and (s1_dcount < 17) -- we haven't exceeded the limit of 18 consecutive packets
+ and (data_req /= "0000") -- another packet is requested
+ then -- do another data packet
+ s1_pcount <= (others => '0');
+ s1_dcount <= s1_dcount+1;
+ for i in 0 to PACKET_TYPES-1 loop -- prioritize
+ p := i;
+ exit when data_req(p) = '1';
+ end loop;
+ data_ack(p) <= '1';
+ s1_hb <= hb(p);
+ s1_sb(0) <= pb(p)(0 to 6);
+ s1_sb(1) <= pb(p)(7 to 13);
+ s1_sb(2) <= pb(p)(14 to 20);
+ s1_sb(3) <= pb(p)(21 to 27);
+ else -- wrap up
+ s1_period <= DATA_GB_TRAILING; s1_enc <= ENC_GB_DATA; s1_ctl <= CTL_NULL;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ end if;
+ end if;
+
+ when DATA_GB_TRAILING =>
+ if s1_pcount(0) = '1' then
+ s1_period <= CONTROL; s1_enc <= ENC_DVI; s1_ctl <= CTL_NULL;
+ s1_pcount <= (others => '0'); s1_dcount <= (others => '0');
+ end if;
+
+ end case;
+
+ s1_hb_byte := to_integer(unsigned(s1_pcount(4 downto 3)));
+ s1_hb_bit := to_integer(unsigned(s1_pcount(2 downto 0)));
+ s1_sb_byte := to_integer(unsigned(s1_pcount(4 downto 2)));
+ s1_sb_2bit := to_integer(unsigned(s1_pcount(1 downto 0)));
+
+ s1_vs <= buf_vs;
+ s1_hs <= buf_hs;
+ s1_de <= buf_de;
+ s1_p <= buf_p;
+
+ -- HDMI encoding pipeline stage 2
+
+ s2_data <= '0';
+ if s1_period = DATA_ISLAND then
+ s2_data <= '1';
+ end if;
+ s2_pcount <= s1_pcount;
+ s2_bch4 <= '0';
+ if s1_hb_byte < s1_hb'length then
+ s2_bch4 <= s1_hb(s1_hb_byte)(s1_hb_bit);
+ end if;
+ for i in 0 to 3 loop
+ s2_bch_e(i) <= '0';
+ s2_bch_o(i) <= '0';
+ if s1_sb_byte < s1_sb(i)'length then
+ s2_bch_e(i) <= s1_sb(i)(s1_sb_byte)(0+(2*s1_sb_2bit));
+ s2_bch_o(i) <= s1_sb(i)(s1_sb_byte)(1+(2*s1_sb_2bit));
+ end if;
+ end loop;
+
+ s2_vs <= s1_vs;
+ s2_hs <= s1_hs;
+ s2_de <= s1_de;
+ s2_p <= s1_p;
+ s2_enc <= s1_enc;
+ s2_ctl <= s1_ctl;
+
+ -- HDMI encoding pipeline stage 3
+
+ s3_data <= s2_data;
+ s3_pcount <= s2_pcount;
+ s3_bch4 <= s2_bch4;
+ s3_bch_e <= s2_bch_e;
+ s3_bch_o <= s2_bch_o;
+ if unsigned(s2_pcount(4 downto 0)) = 0 then
+ s3_bch_ecc(4) <= bch_ecc_1(x"00", s2_bch4);
+ elsif unsigned(s2_pcount(4 downto 0)) < 24 then
+ s3_bch_ecc(4) <= bch_ecc_1(s3_bch_ecc(4), s2_bch4);
+ end if;
+ for i in 0 to 3 loop
+ if unsigned(s2_pcount(4 downto 0)) = 0 then
+ s3_bch_ecc(i) <= bch_ecc_2(x"00", s2_bch_o(i) & s2_bch_e(i));
+ elsif unsigned(s2_pcount(4 downto 0)) < 28 then
+ s3_bch_ecc(i) <= bch_ecc_2(s3_bch_ecc(i), s2_bch_o(i) & s2_bch_e(i));
+ end if;
+ end loop;
+
+ s3_vs <= s2_vs;
+ s3_hs <= s2_hs;
+ s3_de <= s2_de;
+ s3_p <= s2_p;
+ s3_enc <= s2_enc;
+ s3_ctl <= s2_ctl;
+
+ -- HDMI encoding pipeline stage 4
+
+ s4_d <= (others => (others => '0'));
+ if s3_data = '1' then
+ s4_d(0)(0) <= s3_hs;
+ s4_d(0)(1) <= s3_vs;
+ if unsigned(s3_pcount(4 downto 0)) < 24 then
+ s4_d(0)(2) <= s3_bch4;
+ else
+ s4_d(0)(2) <= s3_bch_ecc(4)(to_integer(unsigned(s3_pcount(2 downto 0))));
+ end if;
+ if unsigned(s3_pcount) = 0 then
+ s4_d(0)(3) <= '0';
+ else
+ s4_d(0)(3) <= '1';
+ end if;
+ if unsigned(s3_pcount(4 downto 0)) < 28 then
+ s4_d(1) <= s3_bch_e;
+ s4_d(2) <= s3_bch_o;
+ else
+ for i in 0 to 3 loop
+ s4_d(1)(i) <= s3_bch_ecc(i)(0+(2*to_integer(unsigned(s3_pcount(1 downto 0)))));
+ s4_d(2)(i) <= s3_bch_ecc(i)(1+(2*to_integer(unsigned(s3_pcount(1 downto 0)))));
+ end loop;
+ end if;
+ end if;
+
+ s4_vs <= s3_vs;
+ s4_hs <= s3_hs;
+ s4_de <= s3_de;
+ s4_p <= s3_p;
+ s4_enc <= s3_enc;
+ s4_ctl <= s3_ctl;
+
+ -- DVI override
+
+ if dvi_s = '1' then
+ s4_enc <= ENC_DVI;
+ s4_ctl <= (others => '0');
+ s4_d <= (others => (others => '0'));
+ end if;
+
+ end if;
+
+ end process VGA;
+
+ -- final encoder and serialiser stage
+
+ s4_c(0)(0) <= s4_hs;
+ s4_c(0)(1) <= s4_vs;
+ s4_c(1) <= s4_ctl(1 downto 0);
+ s4_c(2) <= s4_ctl(3 downto 2);
+
+ gen_tmds: for i in 0 to 2 generate
+ begin
+
+ ENCODER: entity work.hdmi_tx_encoder
+ generic map (
+ channel => i
+ )
+ port map (
+ rst => vga_rst,
+ clk => vga_clk,
+ de => s4_de,
+ p => s4_p(i),
+ c => s4_c(i),
+ d => s4_d(i),
+ enc => s4_enc,
+ q => tmds(i)
+ );
+
+ end generate gen_tmds;
+
+end architecture synth;
diff --git a/vhdl/hw/MEGA65/video_modes_pkg.vhd b/vhdl/hw/MEGA65/video_modes_pkg.vhd
new file mode 100644
index 00000000..2cd46015
--- /dev/null
+++ b/vhdl/hw/MEGA65/video_modes_pkg.vhd
@@ -0,0 +1,236 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+package video_modes_pkg is
+
+ type video_modes_t is record
+ CLK_KHZ : integer; -- Pixel clock frequency in kHz
+ CLK_SEL : std_logic_vector(2 downto 0); -- Pixel clock selection
+ -- 000 = 25.200 MHz
+ -- 001 = 27.000 MHz
+ -- 010 = 74.250 MHz
+ -- 011 = 148.500 MHz
+ -- 100 = 25.175 MHz
+ -- 101 = 27.027 MHz
+ -- 110 = 74.176 MHz
+ -- 111 = undefined
+ CEA_CTA_VIC : integer; -- CEA/CTA VIC
+ ASPECT : std_logic_vector(1 downto 0); -- aspect ratio: 01=4:3, 10=16:9
+ PIXEL_REP : std_logic; -- 0=no pixel repetition; 1=pixel repetition
+ H_PIXELS : integer; -- horizontal display width in pixels
+ V_PIXELS : integer; -- vertical display width in rows
+ H_PULSE : integer; -- horizontal sync pulse width in pixels
+ H_BP : integer; -- horizontal back porch width in pixels
+ H_FP : integer; -- horizontal front porch width in pixels
+ V_PULSE : integer; -- vertical sync pulse width in rows
+ V_BP : integer; -- vertical back porch width in rows
+ V_FP : integer; -- vertical front porch width in rows
+ H_POL : std_logic; -- horizontal sync pulse polarity (1 = positive, 0 = negative)
+ V_POL : std_logic; -- vertical sync pulse polarity (1 = positive, 0 = negative)
+ end record video_modes_t;
+
+ -- In the following, the supported video modes
+ -- are sorted according to the CEA-861-D document
+
+ --------------------------------------------------------
+ -- 50 Hz modes
+ --------------------------------------------------------
+
+ -- PAL 720x576 @ 50 Hz
+ -- Taken from section 4.9 in the document CEA-861-D
+ constant C_PAL_720_576_50 : video_modes_t := (
+ CLK_KHZ => 27000, -- 27.000 MHz
+ CLK_SEL => "001",
+ CEA_CTA_VIC => 17, -- CEA/CTA VIC 17=PAL 720x576 @ 50 Hz
+ ASPECT => "01", -- aspect ratio: 01=4:3, 10=16:9: "01" for PAL
+ PIXEL_REP => '0', -- no pixel repetition
+ H_PIXELS => 720, -- horizontal display width in pixels
+ V_PIXELS => 576, -- vertical display width in rows
+ H_PULSE => 64, -- horizontal sync pulse width in pixels
+ H_BP => 63, -- horizontal back porch width in pixels
+ H_FP => 17, -- horizontal front porch width in pixels
+ V_PULSE => 5, -- vertical sync pulse width in rows
+ V_BP => 39, -- vertical back porch width in rows
+ V_FP => 5, -- vertical front porch width in rows
+ H_POL => '0', -- horizontal sync pulse polarity (1 = positive, 0 = negative)
+ V_POL => '0' -- vertical sync pulse polarity (1 = positive, 0 = negative)
+ );
+
+ -- HDMI 576p @ 50 Hz (720x576)
+ -- Taken from section 4.9 in the document CEA-861-D
+ constant C_HDMI_576p_50 : video_modes_t := (
+ CLK_KHZ => 27000, -- 27.000 MHz
+ CLK_SEL => "001",
+ CEA_CTA_VIC => 17, -- CEA/CTA VIC: 720x576p, 50 Hz, 4:3
+ ASPECT => "01", -- apsect ratio: 01=4:3, 10=16:9
+ PIXEL_REP => '0', -- no pixel repetition
+ H_PIXELS => 720, -- horizontal display width in pixels
+ V_PIXELS => 576, -- vertical display width in rows
+ H_FP => 12, -- horizontal front porch width in pixels
+ H_PULSE => 64, -- horizontal sync pulse width in pixels
+ H_BP => 68, -- horizontal back porch width in pixels
+ V_FP => 5, -- vertical front porch width in rows
+ V_PULSE => 5, -- vertical sync pulse width in rows
+ V_BP => 39, -- vertical back porch width in rows
+ H_POL => '1', -- horizontal sync pulse polarity (1 = positive, 0 = negative)
+ V_POL => '1' -- vertical sync pulse polarity (1 = positive, 0 = negative)
+ );
+
+ -- HDMI 720p @ 50 Hz (1280x720)
+ -- Taken from section 4.7 in the document CEA-861-D
+ constant C_HDMI_720p_50 : video_modes_t := (
+ CLK_KHZ => 74250, -- 74.250 MHz
+ CLK_SEL => "010",
+ CEA_CTA_VIC => 19, -- CEA/CTA VIC: 1280x720p, 50 Hz, 16:9
+ ASPECT => "10", -- apsect ratio: 01=4:3, 10=16:9
+ PIXEL_REP => '0', -- no pixel repetition
+ H_PIXELS => 1280, -- horizontal display width in pixels
+ V_PIXELS => 720, -- vertical display width in rows
+ H_FP => 440, -- horizontal front porch width in pixels
+ H_PULSE => 40, -- horizontal sync pulse width in pixels
+ H_BP => 220, -- horizontal back porch width in pixels
+ V_FP => 5, -- vertical front porch width in rows
+ V_PULSE => 5, -- vertical sync pulse width in rows
+ V_BP => 20, -- vertical back porch width in rows
+ H_POL => '1', -- horizontal sync pulse polarity (1 = positive, 0 = negative)
+ V_POL => '1' -- vertical sync pulse polarity (1 = positive, 0 = negative)
+ );
+
+
+ --------------------------------------------------------
+ -- 59.94 Hz modes
+ --------------------------------------------------------
+
+ -- HDMI 480p @ 59.94 Hz (720x480)
+ constant C_HDMI_720x480p_5994 : video_modes_t := (
+ CLK_KHZ => 27000, -- 27.000 MHz
+ CLK_SEL => "001",
+ CEA_CTA_VIC => 2,
+ ASPECT => "01", -- apsect ratio: 01=4:3, 10=16:9
+ PIXEL_REP => '0', -- no pixel repetition
+ H_PIXELS => 720, -- horizontal display width in pixels
+ V_PIXELS => 480, -- vertical display width in rows
+ H_FP => 16, -- horizontal front porch width in pixels
+ H_PULSE => 62, -- horizontal sync pulse width in pixels
+ H_BP => 60, -- horizontal back porch width in pixels
+ V_FP => 9, -- vertical front porch width in rows
+ V_PULSE => 6, -- vertical sync pulse width in rows
+ V_BP => 30, -- vertical back porch width in rows
+ H_POL => '0', -- horizontal sync pulse polarity (1 = positive, 0 = negative)
+ V_POL => '0' -- vertical sync pulse polarity (1 = positive, 0 = negative)
+ );
+
+
+ --------------------------------------------------------
+ -- 60.00 Hz modes
+ --------------------------------------------------------
+
+ -- HDMI 480p @ 60 Hz (640x480)
+ constant C_HDMI_640x480p_60 : video_modes_t := (
+ CLK_KHZ => 25200, -- 25.200 MHz
+ CLK_SEL => "000",
+ CEA_CTA_VIC => 1,
+ ASPECT => "01", -- apsect ratio: 01=4:3, 10=16:9
+ PIXEL_REP => '0', -- no pixel repetition
+ H_PIXELS => 640, -- horizontal display width in pixels
+ V_PIXELS => 480, -- vertical display width in rows
+ H_FP => 16, -- horizontal front porch width in pixels
+ H_PULSE => 96, -- horizontal sync pulse width in pixels
+ H_BP => 48, -- horizontal back porch width in pixels
+ V_FP => 10, -- vertical front porch width in rows
+ V_PULSE => 2, -- vertical sync pulse width in rows
+ V_BP => 33, -- vertical back porch width in rows
+ H_POL => '0', -- horizontal sync pulse polarity (1 = positive, 0 = negative)
+ V_POL => '0' -- vertical sync pulse polarity (1 = positive, 0 = negative)
+ );
+
+ -- HDMI 720p @ 60 Hz (1280x720)
+ -- Taken from section 4.3 in the document CEA-861-D
+ constant C_HDMI_720p_60 : video_modes_t := (
+ CLK_KHZ => 74250, -- 74.250 MHz
+ CLK_SEL => "010",
+ CEA_CTA_VIC => 4, -- CEA/CTA VIC: 1280x720p, 60 Hz, 16:9
+ ASPECT => "10", -- apsect ratio: 01=4:3, 10=16:9
+ PIXEL_REP => '0', -- no pixel repetition
+ H_PIXELS => 1280, -- horizontal display width in pixels
+ V_PIXELS => 720, -- vertical display width in rows
+ H_FP => 110, -- horizontal front porch width in pixels
+ H_PULSE => 40, -- horizontal sync pulse width in pixels
+ H_BP => 220, -- horizontal back porch width in pixels
+ V_FP => 5, -- vertical front porch width in rows
+ V_PULSE => 5, -- vertical sync pulse width in rows
+ V_BP => 20, -- vertical back porch width in rows
+ H_POL => '0', -- horizontal sync pulse polarity (1 = positive, 0 = negative)
+ V_POL => '0' -- vertical sync pulse polarity (1 = positive, 0 = negative)
+ );
+
+ -- SVGA 800x600 @ 60 Hz
+ -- Taken from this link: http://tinyvga.com/vga-timing/800x600@60Hz
+ -- CAUTION: CTA/CTV VIC does not officially support SVGA 800x600; there are some monitors, where it works, though
+ constant C_SVGA_800_600_60 : video_modes_t := (
+ CLK_KHZ => 40000, -- 40.000 MHz
+ CLK_SEL => "111",
+ CEA_CTA_VIC => 65, -- SVGA is not an official mode; "65" taken from here: https://www.raspberrypi.org/documentation/configuration/config-txt/video.md
+ ASPECT => "01", -- aspect ratio: 01=4:3, 10=16:9: "01" for SVGA
+ PIXEL_REP => '0', -- no pixel repetition
+ H_PIXELS => 800, -- horizontal display width in pixels
+ V_PIXELS => 600, -- vertical display width in rows
+ H_PULSE => 128, -- horizontal sync pulse width in pixels
+ H_BP => 88, -- horizontal back porch width in pixels
+ H_FP => 40, -- horizontal front porch width in pixels
+ V_PULSE => 4, -- vertical sync pulse width in rows
+ V_BP => 23, -- vertical back porch width in rows
+ V_FP => 1, -- vertical front porch width in rows
+ H_POL => '1', -- horizontal sync pulse polarity (1 = positive, 0 = negative)
+ V_POL => '1' -- vertical sync pulse polarity (1 = positive, 0 = negative)
+ );
+
+ type video_modes_vector is array(natural range<>) of video_modes_t;
+
+ type video_mode_type is (
+ C_VIDEO_HDMI_16_9_50 , -- HDMI 1280x720 @ 50 Hz
+ C_VIDEO_HDMI_16_9_60 , -- HDMI 1280x720 @ 60 Hz
+ C_VIDEO_HDMI_4_3_50 , -- PAL 576p in 4:3 @ 50 Hz
+ C_VIDEO_HDMI_5_4_50 , -- PAL 576p in 5:4 @ 50 Hz
+ C_VIDEO_HDMI_640_60 , -- HDMI 640x480 @ 60 Hz
+ C_VIDEO_HDMI_720_5994 , -- HDMI 720x480 @ 59.94 Hz
+ C_VIDEO_SVGA_800_60 -- SVGA 800x600 @ 60 Hz
+ );
+
+ pure function video_mode_to_slv(video_mode : video_mode_type) return std_logic_vector;
+
+ pure function slv_to_video_mode(video_mode_slv : std_logic_vector) return video_mode_type;
+
+end package video_modes_pkg;
+
+package body video_modes_pkg is
+
+ pure function video_mode_to_slv(video_mode : video_mode_type) return std_logic_vector is
+ begin
+ case video_mode is
+ when C_VIDEO_HDMI_16_9_50 => return "0000";
+ when C_VIDEO_HDMI_16_9_60 => return "0001";
+ when C_VIDEO_HDMI_4_3_50 => return "0010";
+ when C_VIDEO_HDMI_5_4_50 => return "0011";
+ when C_VIDEO_HDMI_640_60 => return "0100";
+ when C_VIDEO_HDMI_720_5994 => return "0101";
+ when C_VIDEO_SVGA_800_60 => return "0110";
+ end case;
+ end function video_mode_to_slv;
+
+ pure function slv_to_video_mode(video_mode_slv : std_logic_vector) return video_mode_type is
+ begin
+ case video_mode_slv is
+ when "0000" => return C_VIDEO_HDMI_16_9_50;
+ when "0001" => return C_VIDEO_HDMI_16_9_60;
+ when "0010" => return C_VIDEO_HDMI_4_3_50;
+ when "0011" => return C_VIDEO_HDMI_5_4_50;
+ when "0100" => return C_VIDEO_HDMI_640_60;
+ when "0101" => return C_VIDEO_HDMI_720_5994;
+ when "0110" => return C_VIDEO_SVGA_800_60;
+ when others => return C_VIDEO_HDMI_16_9_50;
+ end case;
+ end function slv_to_video_mode;
+
+end package body video_modes_pkg;
+