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