From 78c1dae8212afdd81df8aebbece895f9bf2796b4 Mon Sep 17 00:00:00 2001 From: Mike Stirling Date: Sun, 17 Jul 2011 21:32:44 +0100 Subject: Work in progress: Added MOS6522 from www.fpgaarcade.com. Added simple_uart component in "FRED" for test purposes and added alternative EHBASIC ROM for debugging --- bbc_micro_de1.qsf | 11 +- bbc_micro_de1.vhd | 327 +++++++++++++++++++- ehbasic.qip | 3 + ehbasic.vhd | 168 +++++++++++ m6522.vhd | 889 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ simple_uart.vhd | 233 ++++++++++++++ 6 files changed, 1613 insertions(+), 18 deletions(-) create mode 100644 ehbasic.qip create mode 100644 ehbasic.vhd create mode 100644 m6522.vhd create mode 100644 simple_uart.vhd diff --git a/bbc_micro_de1.qsf b/bbc_micro_de1.qsf index 72b1ee2..d6578f0 100644 --- a/bbc_micro_de1.qsf +++ b/bbc_micro_de1.qsf @@ -494,6 +494,12 @@ set_location_assignment PIN_T8 -to SRAM_OE_N set_location_assignment PIN_W7 -to SRAM_UB_N set_location_assignment PIN_AA10 -to SRAM_WE_N set_global_assignment -name RESERVE_ALL_UNUSED_PINS "AS INPUT TRI-STATED WITH WEAK PULL-UP" +set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top +set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top +set_global_assignment -name LL_ROOT_REGION ON -section_id "Root Region" +set_global_assignment -name LL_MEMBER_STATE LOCKED -section_id "Root Region" +set_global_assignment -name VHDL_FILE simple_uart.vhd +set_global_assignment -name VHDL_FILE m6522.vhd set_global_assignment -name VHDL_FILE seg7.vhd set_global_assignment -name VHDL_FILE vidproc.vhd set_global_assignment -name VHDL_FILE mc6845.vhd @@ -504,9 +510,6 @@ set_global_assignment -name VHDL_FILE T65/T65_MCode.vhd set_global_assignment -name QIP_FILE os12.qip set_global_assignment -name QIP_FILE pll32.qip set_global_assignment -name VHDL_FILE bbc_micro_de1.vhd -set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top -set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top -set_global_assignment -name LL_ROOT_REGION ON -section_id "Root Region" -set_global_assignment -name LL_MEMBER_STATE LOCKED -section_id "Root Region" set_global_assignment -name VHDL_FILE bbc_micro_de1_tb.vhd +set_global_assignment -name QIP_FILE ehbasic.qip set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/bbc_micro_de1.vhd b/bbc_micro_de1.vhd index d854582..f48507f 100644 --- a/bbc_micro_de1.vhd +++ b/bbc_micro_de1.vhd @@ -122,6 +122,35 @@ port ( ); end component; +------------------------------ +-- Test UART for custom ROM +------------------------------ + +component simple_uart is +generic ( + f_clock : natural := 32000000; + baud_rate : natural := 19200 + ); +port ( + CLOCK : in std_logic; + nRESET : in std_logic; + + ENABLE : in std_logic; + -- Read not write + R_nW : in std_logic; + -- Data not status (address) + S_nD : in std_logic; + -- Data bus in + DI : in std_logic_vector(7 downto 0); + -- Data bus out + DO : out std_logic_vector(7 downto 0); + + -- Port pins + RXD : in std_logic; + TXD : out std_logic + ); +end component; + --------- -- CPU --------- @@ -234,6 +263,67 @@ component os12 IS ); end component; +-------------- +-- Test ROM +-------------- + +component ehbasic IS + PORT + ( + address : IN STD_LOGIC_VECTOR (13 DOWNTO 0); + clock : IN STD_LOGIC ; + q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0) + ); +end component; + +-------------- +-- 6522 VIA +-------------- + +component M6522 is + port ( + + I_RS : in std_logic_vector(3 downto 0); + I_DATA : in std_logic_vector(7 downto 0); + O_DATA : out std_logic_vector(7 downto 0); + O_DATA_OE_L : out std_logic; + + I_RW_L : in std_logic; + I_CS1 : in std_logic; + I_CS2_L : in std_logic; + + O_IRQ_L : out std_logic; -- note, not open drain + -- port a + I_CA1 : in std_logic; + I_CA2 : in std_logic; + O_CA2 : out std_logic; + O_CA2_OE_L : out std_logic; + + I_PA : in std_logic_vector(7 downto 0); + O_PA : out std_logic_vector(7 downto 0); + O_PA_OE_L : out std_logic_vector(7 downto 0); + + -- port b + I_CB1 : in std_logic; + O_CB1 : out std_logic; + O_CB1_OE_L : out std_logic; + + I_CB2 : in std_logic; + O_CB2 : out std_logic; + O_CB2_OE_L : out std_logic; + + I_PB : in std_logic_vector(7 downto 0); + O_PB : out std_logic_vector(7 downto 0); + O_PB_OE_L : out std_logic_vector(7 downto 0); + + -- FIXME: Revisit timing in this component. Does it really need a 4x clock? + I_P2_H : in std_logic; -- high for phase 2 clock ____----__ + RESET_L : in std_logic; + ENA_4 : in std_logic; -- clk enable (4x system clock rate) + CLK : in std_logic + ); +end component; + ------------- -- Signals ------------- @@ -252,6 +342,9 @@ signal slomo_counter : unsigned(17 downto 0); signal cpu_clken : std_logic; signal vid_clken : std_logic; +-- Testing +signal test_uart_do : std_logic_vector(7 downto 0); + -- CPU signals signal cpu_mode : std_logic_vector(1 downto 0); signal cpu_ready : std_logic; @@ -279,7 +372,7 @@ signal crtc_vsync : std_logic; signal crtc_hsync : std_logic; signal crtc_de : std_logic; signal crtc_cursor : std_logic; -signal crtc_lpstb : std_logic; +signal crtc_lpstb : std_logic := '0'; signal crtc_ma : std_logic_vector(13 downto 0); signal crtc_ra : std_logic_vector(4 downto 0); @@ -295,6 +388,62 @@ signal b_out : std_logic; -- MOS ROM signals signal mos_d : std_logic_vector(7 downto 0); +-- System VIA signals +signal sys_via_do : std_logic_vector(7 downto 0); +signal sys_via_do_oe_n : std_logic; +signal sys_via_irq_n : std_logic; +signal sys_via_ca1_in : std_logic := '0'; +signal sys_via_ca2_in : std_logic := '0'; +signal sys_via_ca2_out : std_logic; +signal sys_via_ca2_oe_n : std_logic; +signal sys_via_pa_in : std_logic_vector(7 downto 0); +signal sys_via_pa_out : std_logic_vector(7 downto 0); +signal sys_via_pa_oe_n : std_logic_vector(7 downto 0); +signal sys_via_cb1_in : std_logic := '0'; +signal sys_via_cb1_out : std_logic; +signal sys_via_cb1_oe_n : std_logic; +signal sys_via_cb2_in : std_logic := '0'; +signal sys_via_cb2_out : std_logic; +signal sys_via_cb2_oe_n : std_logic; +signal sys_via_pb_in : std_logic_vector(7 downto 0); +signal sys_via_pb_out : std_logic_vector(7 downto 0); +signal sys_via_pb_oe_n : std_logic_vector(7 downto 0); + +-- User VIA signals +signal user_via_do : std_logic_vector(7 downto 0); +signal user_via_do_oe_n : std_logic; +signal user_via_irq_n : std_logic; +signal user_via_ca1_in : std_logic := '0'; +signal user_via_ca2_in : std_logic := '0'; +signal user_via_ca2_out : std_logic; +signal user_via_ca2_oe_n : std_logic; +signal user_via_pa_in : std_logic_vector(7 downto 0); +signal user_via_pa_out : std_logic_vector(7 downto 0); +signal user_via_pa_oe_n : std_logic_vector(7 downto 0); +signal user_via_cb1_in : std_logic := '0'; +signal user_via_cb1_out : std_logic; +signal user_via_cb1_oe_n : std_logic; +signal user_via_cb2_in : std_logic := '0'; +signal user_via_cb2_out : std_logic; +signal user_via_cb2_oe_n : std_logic; +signal user_via_pb_in : std_logic_vector(7 downto 0); +signal user_via_pb_out : std_logic_vector(7 downto 0); +signal user_via_pb_oe_n : std_logic_vector(7 downto 0); + +-- Common VIA signals +signal via_clken : std_logic; +signal via_phase2 : std_logic; + +-- IC32 latch on System VIA +signal ic32 : std_logic_vector(7 downto 0); +signal sound_enable_n : std_logic; +signal speech_read_n : std_logic; +signal speech_write_n : std_logic; +signal keyb_enable_n : std_logic; +signal disp_addr_offs : std_logic_vector(1 downto 0); +signal caps_lock_led_n : std_logic; +signal shift_lock_led_n : std_logic; + -- Memory enables signal ram_enable : std_logic; -- 0x0000 signal rom_enable : std_logic; -- 0x8000 (BASIC/sideways ROMs) @@ -316,9 +465,6 @@ signal adlc_enable : std_logic; -- 0xFEA0-FEBF (Econet) signal adc_enable : std_logic; -- 0xFEC0-FEDF signal tube_enable : std_logic; -- 0xFEE0-FEFF --- Temporary hack -signal kblink_enable : std_logic; - begin ------------------------- -- COMPONENT INSTANCES @@ -336,6 +482,20 @@ begin addr2 : seg7 port map (cpu_a(11 downto 8), HEX2); addr1 : seg7 port map (cpu_a(7 downto 4), HEX1); addr0 : seg7 port map (cpu_a(3 downto 0), HEX0); + + -- Test UART + + test_uart : simple_uart port map ( + clock, + reset_n, + io_fred, + cpu_r_nw, + cpu_a(0), + cpu_do, + test_uart_do, + UART_RXD, + UART_TXD + ); -- 6502 CPU cpu : T65 port map ( @@ -395,18 +555,91 @@ begin ); -- MOS ROM - mos : os12 port map ( +-- mos : os12 port map ( +-- cpu_a(13 downto 0), +-- clock, +-- mos_d ); + test_rom : ehbasic port map ( cpu_a(13 downto 0), - clock, - mos_d ); + clock, mos_d ); + + -- System VIA + system_via : m6522 port map ( + cpu_a(3 downto 0), + cpu_do, + sys_via_do, + sys_via_do_oe_n, + cpu_r_nw, + sys_via_enable, + '0', -- nCS2 + sys_via_irq_n, + sys_via_ca1_in, + sys_via_ca2_in, + sys_via_ca2_out, + sys_via_ca2_oe_n, + sys_via_pa_in, + sys_via_pa_out, + sys_via_pa_oe_n, + sys_via_cb1_in, + sys_via_cb1_out, + sys_via_cb1_oe_n, + sys_via_cb2_in, + sys_via_cb2_out, + sys_via_cb2_oe_n, + sys_via_pb_in, + sys_via_pb_out, + sys_via_pb_oe_n, + via_phase2, + reset_n, + via_clken, + clock + ); + + -- User VIA + user_via : m6522 port map ( + cpu_a(3 downto 0), + cpu_do, + user_via_do, + user_via_do_oe_n, + cpu_r_nw, + user_via_enable, + '0', -- nCS2 + user_via_irq_n, + user_via_ca1_in, + user_via_ca2_in, + user_via_ca2_out, + user_via_ca2_oe_n, + user_via_pa_in, + user_via_pa_out, + user_via_pa_oe_n, + user_via_cb1_in, + user_via_cb1_out, + user_via_cb1_oe_n, + user_via_cb2_in, + user_via_cb2_out, + user_via_cb2_oe_n, + user_via_pb_in, + user_via_pb_out, + user_via_pb_oe_n, + via_phase2, + reset_n, + via_clken, + clock + ); -- Asynchronous reset pll_reset <= not SW(9); reset_n <= not (pll_reset or not pll_locked); -- Clock enable generation + -- CPU is on cycle 0 of 16 (2 MHz) + -- Video is on all odd cycles cpu_clken <= '1' when clken_counter = 0 and (SW(8) = '1' or slomo_counter = 0) else '0'; vid_clken <= clken_counter(0); + -- FIXME: VIAs - this is wrong. They should actually run at 1MHz (so 4 MHz clken in this + -- case) and the CPU should be stalled until the 1 MHz bus cycle is complete + via_clken <= clken_counter(0) and clken_counter(1); + via_phase2 <= clken_counter(3); process(clock,reset_n) begin if reset_n = '0' then @@ -424,7 +657,6 @@ begin cpu_mode <= "00"; -- 6502 cpu_ready <= '1'; cpu_abort_n <= '1'; - cpu_irq_n <= '1'; cpu_nmi_n <= '1'; cpu_so_n <= '1'; @@ -444,9 +676,6 @@ begin io_jim <= '1' when cpu_a(15 downto 8) = "11111101" else '0'; io_sheila <= '1' when cpu_a(15 downto 8) = "11111110" else '0'; - -- Temporary hack for keyboard links - kblink_enable <= '1' when cpu_a(15 downto 0) = "0000001010001111" else '0'; - -- SHEILA address demux -- All the system peripherals are mapped into this page as follows: -- 0xFE00 - 0xFE07 = MC6845 CRTC @@ -512,13 +741,16 @@ begin end if; end process; - -- CPU data bus mux + -- CPU data bus mux and interrupts cpu_di <= mos_d when mos_enable = '1' else "11111111" when rom_enable = '1' else crtc_do when crtc_enable = '1' else - SW(7 downto 0) when kblink_enable = '1' else + sys_via_do when sys_via_enable = '1' else + user_via_do when user_via_enable = '1' else + test_uart_do when io_fred = '1' else SRAM_DQ(7 downto 0); + cpu_irq_n <= sys_via_irq_n and user_via_irq_n; -- SRAM bus SRAM_UB_N <= '1'; @@ -565,7 +797,7 @@ begin GPIO_0(0) <= crtc_clken; GPIO_0(1) <= crtc_hsync; GPIO_0(2) <= crtc_vsync; - GPIO_0(3) <= crtc_de; + GPIO_0(3) <= not (crtc_hsync xor crtc_vsync); -- CRTC drives video out (CSYNC on HSYNC output, VSYNC high) VGA_HS <= not (crtc_hsync xor crtc_vsync); @@ -573,5 +805,72 @@ begin VGA_R <= r_out & r_out & r_out & r_out; VGA_G <= g_out & g_out & g_out & g_out; VGA_B <= b_out & b_out & b_out & b_out; + + -- Connections to System VIA + sys_via_ca1_in <= crtc_vsync; + sys_via_cb1_in <= crtc_lpstb; + + -- Connections to User VIA (user port is output on green LEDs) + --LEDG <= user_via_pb_out; + + -- IC32 latch + sound_enable_n <= ic32(0); + speech_write_n <= ic32(1); + speech_read_n <= ic32(2); + keyb_enable_n <= ic32(3); + disp_addr_offs <= ic32(5 downto 4); + caps_lock_led_n <= ic32(6); + shift_lock_led_n <= ic32(7); + + process(clock,reset_n) + variable bit_num : integer; + begin + bit_num := to_integer(unsigned(sys_via_pb_out(2 downto 0))); + + if reset_n = '0' then + ic32 <= (others => '0'); + elsif rising_edge(clock) then + ic32(bit_num) <= sys_via_pb_out(3); + end if; + end process; + + LEDG <= ic32; + + -- Keyboard LEDs + LEDR(0) <= not caps_lock_led_n; + LEDR(1) <= not shift_lock_led_n; + + -- Hack for jumper links + process(sys_via_pa_out) + begin + sys_via_pa_in <= sys_via_pa_out; + + if sys_via_pa_out(6 downto 4) = "000" then + case to_integer(unsigned(sys_via_pa_out(3 downto 0))) is + when 2 => sys_via_pa_in(7) <= SW(7); + when 3 => sys_via_pa_in(7) <= SW(6); + when 4 => sys_via_pa_in(7) <= SW(5); + when 5 => sys_via_pa_in(7) <= SW(4); + when 6 => sys_via_pa_in(7) <= SW(3); + when 7 => sys_via_pa_in(7) <= SW(2); + when 8 => sys_via_pa_in(7) <= SW(1); + when 9 => sys_via_pa_in(7) <= SW(0); + when others => sys_via_pa_in(7) <= '1'; + end case; + else + sys_via_pa_in(7) <= '1'; + end if; + end process; + +-- process(clock,reset_n) +-- begin +-- if reset_n = '0' then +-- LEDG <= "00000000"; +-- elsif rising_edge(clock) then +-- if cpu_a(15 downto 0) = "0000001010001111" and cpu_r_nw = '0' then +-- LEDG <= cpu_do; +-- end if; +-- end if; +-- end process; end architecture; diff --git a/ehbasic.qip b/ehbasic.qip new file mode 100644 index 0000000..3221c1f --- /dev/null +++ b/ehbasic.qip @@ -0,0 +1,3 @@ +set_global_assignment -name IP_TOOL_NAME "ROM: 1-PORT" +set_global_assignment -name IP_TOOL_VERSION "9.1" +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "ehbasic.vhd"] diff --git a/ehbasic.vhd b/ehbasic.vhd new file mode 100644 index 0000000..1ab195b --- /dev/null +++ b/ehbasic.vhd @@ -0,0 +1,168 @@ +-- megafunction wizard: %ROM: 1-PORT% +-- GENERATION: STANDARD +-- VERSION: WM1.0 +-- MODULE: altsyncram + +-- ============================================================ +-- File Name: ehbasic.vhd +-- Megafunction Name(s): +-- altsyncram +-- +-- Simulation Library Files(s): +-- altera_mf +-- ============================================================ +-- ************************************************************ +-- THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +-- +-- 9.1 Build 222 10/21/2009 SJ Web Edition +-- ************************************************************ + + +--Copyright (C) 1991-2009 Altera Corporation +--Your use of Altera Corporation's design tools, logic functions +--and other software and tools, and its AMPP partner logic +--functions, and any output files from any of the foregoing +--(including device programming or simulation files), and any +--associated documentation or information are expressly subject +--to the terms and conditions of the Altera Program License +--Subscription Agreement, Altera MegaCore Function License +--Agreement, or other applicable license agreement, including, +--without limitation, that your use is for the sole purpose of +--programming logic devices manufactured by Altera and sold by +--Altera or its authorized distributors. Please refer to the +--applicable agreement for further details. + + +LIBRARY ieee; +USE ieee.std_logic_1164.all; + +LIBRARY altera_mf; +USE altera_mf.all; + +ENTITY ehbasic IS + PORT + ( + address : IN STD_LOGIC_VECTOR (13 DOWNTO 0); + clock : IN STD_LOGIC ; + q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0) + ); +END ehbasic; + + +ARCHITECTURE SYN OF ehbasic IS + + SIGNAL sub_wire0 : STD_LOGIC_VECTOR (7 DOWNTO 0); + + + + COMPONENT altsyncram + GENERIC ( + clock_enable_input_a : STRING; + clock_enable_output_a : STRING; + init_file : STRING; + intended_device_family : STRING; + lpm_hint : STRING; + lpm_type : STRING; + numwords_a : NATURAL; + operation_mode : STRING; + outdata_aclr_a : STRING; + outdata_reg_a : STRING; + widthad_a : NATURAL; + width_a : NATURAL; + width_byteena_a : NATURAL + ); + PORT ( + clock0 : IN STD_LOGIC ; + address_a : IN STD_LOGIC_VECTOR (13 DOWNTO 0); + q_a : OUT STD_LOGIC_VECTOR (7 DOWNTO 0) + ); + END COMPONENT; + +BEGIN + q <= sub_wire0(7 DOWNTO 0); + + altsyncram_component : altsyncram + GENERIC MAP ( + clock_enable_input_a => "BYPASS", + clock_enable_output_a => "BYPASS", + init_file => "./roms/asm/basic/ehbasic.hex", + intended_device_family => "Cyclone II", + lpm_hint => "ENABLE_RUNTIME_MOD=NO", + lpm_type => "altsyncram", + numwords_a => 16384, + operation_mode => "ROM", + outdata_aclr_a => "NONE", + outdata_reg_a => "UNREGISTERED", + widthad_a => 14, + width_a => 8, + width_byteena_a => 1 + ) + PORT MAP ( + clock0 => clock, + address_a => address, + q_a => sub_wire0 + ); + + + +END SYN; + +-- ============================================================ +-- CNX file retrieval info +-- ============================================================ +-- Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0" +-- Retrieval info: PRIVATE: AclrAddr NUMERIC "0" +-- Retrieval info: PRIVATE: AclrByte NUMERIC "0" +-- Retrieval info: PRIVATE: AclrOutput NUMERIC "0" +-- Retrieval info: PRIVATE: BYTE_ENABLE NUMERIC "0" +-- Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8" +-- Retrieval info: PRIVATE: BlankMemory NUMERIC "0" +-- Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0" +-- Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0" +-- Retrieval info: PRIVATE: Clken NUMERIC "0" +-- Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0" +-- Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_A" +-- Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0" +-- Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone II" +-- Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0" +-- Retrieval info: PRIVATE: JTAG_ID STRING "NONE" +-- Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0" +-- Retrieval info: PRIVATE: MIFfilename STRING "./roms/asm/basic/ehbasic.hex" +-- Retrieval info: PRIVATE: NUMWORDS_A NUMERIC "16384" +-- Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0" +-- Retrieval info: PRIVATE: RegAddr NUMERIC "1" +-- Retrieval info: PRIVATE: RegOutput NUMERIC "0" +-- Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" +-- Retrieval info: PRIVATE: SingleClock NUMERIC "1" +-- Retrieval info: PRIVATE: UseDQRAM NUMERIC "0" +-- Retrieval info: PRIVATE: WidthAddr NUMERIC "14" +-- Retrieval info: PRIVATE: WidthData NUMERIC "8" +-- Retrieval info: PRIVATE: rden NUMERIC "0" +-- Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS" +-- Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS" +-- Retrieval info: CONSTANT: INIT_FILE STRING "./roms/asm/basic/ehbasic.hex" +-- Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone II" +-- Retrieval info: CONSTANT: LPM_HINT STRING "ENABLE_RUNTIME_MOD=NO" +-- Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram" +-- Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "16384" +-- Retrieval info: CONSTANT: OPERATION_MODE STRING "ROM" +-- Retrieval info: CONSTANT: OUTDATA_ACLR_A STRING "NONE" +-- Retrieval info: CONSTANT: OUTDATA_REG_A STRING "UNREGISTERED" +-- Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "14" +-- Retrieval info: CONSTANT: WIDTH_A NUMERIC "8" +-- Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1" +-- Retrieval info: USED_PORT: address 0 0 14 0 INPUT NODEFVAL address[13..0] +-- Retrieval info: USED_PORT: clock 0 0 0 0 INPUT NODEFVAL clock +-- Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL q[7..0] +-- Retrieval info: CONNECT: @address_a 0 0 14 0 address 0 0 14 0 +-- Retrieval info: CONNECT: q 0 0 8 0 @q_a 0 0 8 0 +-- Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0 +-- Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +-- Retrieval info: GEN_FILE: TYPE_NORMAL ehbasic.vhd TRUE +-- Retrieval info: GEN_FILE: TYPE_NORMAL ehbasic.inc FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL ehbasic.cmp FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL ehbasic.bsf FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL ehbasic_inst.vhd FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL ehbasic_waveforms.html FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL ehbasic_wave*.jpg FALSE +-- Retrieval info: LIB_FILE: altera_mf diff --git a/m6522.vhd b/m6522.vhd new file mode 100644 index 0000000..a49e5b5 --- /dev/null +++ b/m6522.vhd @@ -0,0 +1,889 @@ +-- +-- A simulation model of VIC20 hardware +-- Copyright (c) MikeJ - March 2003 +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- You are responsible for any legal issues arising from your use of this code. +-- +-- The latest version of this file can be found at: www.fpgaarcade.com +-- +-- Email vic20@fpgaarcade.com +-- +-- +-- Revision list +-- +-- version 002 fix from Mark McDougall, untested +-- version 001 initial release +-- not very sure about the shift register, documentation is a bit light. + +library ieee ; + use ieee.std_logic_1164.all ; + use ieee.std_logic_unsigned.all; + use ieee.numeric_std.all; + +--library UNISIM; +-- use UNISIM.Vcomponents.all; + +entity M6522 is + port ( + + I_RS : in std_logic_vector(3 downto 0); + I_DATA : in std_logic_vector(7 downto 0); + O_DATA : out std_logic_vector(7 downto 0); + O_DATA_OE_L : out std_logic; + + I_RW_L : in std_logic; + I_CS1 : in std_logic; + I_CS2_L : in std_logic; + + O_IRQ_L : out std_logic; -- note, not open drain + -- port a + I_CA1 : in std_logic; + I_CA2 : in std_logic; + O_CA2 : out std_logic; + O_CA2_OE_L : out std_logic; + + I_PA : in std_logic_vector(7 downto 0); + O_PA : out std_logic_vector(7 downto 0); + O_PA_OE_L : out std_logic_vector(7 downto 0); + + -- port b + I_CB1 : in std_logic; + O_CB1 : out std_logic; + O_CB1_OE_L : out std_logic; + + I_CB2 : in std_logic; + O_CB2 : out std_logic; + O_CB2_OE_L : out std_logic; + + I_PB : in std_logic_vector(7 downto 0); + O_PB : out std_logic_vector(7 downto 0); + O_PB_OE_L : out std_logic_vector(7 downto 0); + + I_P2_H : in std_logic; -- high for phase 2 clock ____----__ + RESET_L : in std_logic; + ENA_4 : in std_logic; -- clk enable + CLK : in std_logic + ); +end; + +architecture RTL of M6522 is + + signal phase : std_logic_vector(1 downto 0); + signal p2_h_t1 : std_logic; + signal cs : std_logic; + + -- registers + signal r_ddra : std_logic_vector(7 downto 0); + signal r_ora : std_logic_vector(7 downto 0); + signal r_ira : std_logic_vector(7 downto 0); + + signal r_ddrb : std_logic_vector(7 downto 0); + signal r_orb : std_logic_vector(7 downto 0); + signal r_irb : std_logic_vector(7 downto 0); + + signal r_t1l_l : std_logic_vector(7 downto 0); + signal r_t1l_h : std_logic_vector(7 downto 0); + signal r_t2l_l : std_logic_vector(7 downto 0); + signal r_t2l_h : std_logic_vector(7 downto 0); -- not in real chip + signal r_sr : std_logic_vector(7 downto 0); + signal r_acr : std_logic_vector(7 downto 0); + signal r_pcr : std_logic_vector(7 downto 0); + signal r_ifr : std_logic_vector(7 downto 0); + signal r_ier : std_logic_vector(6 downto 0); + + signal sr_write_ena : boolean; + signal sr_read_ena : boolean; + signal ifr_write_ena : boolean; + signal ier_write_ena : boolean; + signal clear_irq : std_logic_vector(7 downto 0); + signal load_data : std_logic_vector(7 downto 0); + + -- timer 1 + signal t1c : std_logic_vector(15 downto 0); + signal t1c_active : boolean; + signal t1c_done : boolean; + signal t1_w_reset_int : boolean; + signal t1_r_reset_int : boolean; + signal t1_load_counter : boolean; + signal t1_reload_counter : boolean; + signal t1_toggle : std_logic; + signal t1_irq : std_logic := '0'; + + -- timer 2 + signal t2c : std_logic_vector(15 downto 0); + signal t2c_active : boolean; + signal t2c_done : boolean; + signal t2_pb6 : std_logic; + signal t2_pb6_t1 : std_logic; + signal t2_w_reset_int : boolean; + signal t2_r_reset_int : boolean; + signal t2_load_counter : boolean; + signal t2_reload_counter : boolean; + signal t2_irq : std_logic := '0'; + signal t2_sr_ena : boolean; + + -- shift reg + signal sr_cnt : std_logic_vector(3 downto 0); + signal sr_cb1_oe_l : std_logic; + signal sr_cb1_out : std_logic; + signal sr_drive_cb2 : std_logic; + signal sr_strobe : std_logic; + signal sr_strobe_t1 : std_logic; + signal sr_strobe_falling : boolean; + signal sr_strobe_rising : boolean; + signal sr_irq : std_logic; + signal sr_out : std_logic; + signal sr_off_delay : std_logic; + + -- io + signal w_orb_hs : std_logic; + signal w_ora_hs : std_logic; + signal r_irb_hs : std_logic; + signal r_ira_hs : std_logic; + + signal ca_hs_sr : std_logic; + signal ca_hs_pulse : std_logic; + signal cb_hs_sr : std_logic; + signal cb_hs_pulse : std_logic; + + signal cb1_in_mux : std_logic; + signal ca1_ip_reg : std_logic; + signal cb1_ip_reg : std_logic; + signal ca1_int : boolean; + signal cb1_int : boolean; + signal ca1_irq : std_logic; + signal cb1_irq : std_logic; + + signal ca2_ip_reg : std_logic; + signal cb2_ip_reg : std_logic; + signal ca2_int : boolean; + signal cb2_int : boolean; + signal ca2_irq : std_logic; + signal cb2_irq : std_logic; + + signal final_irq : std_logic; +begin + + p_phase : process + begin + -- internal clock phase + wait until rising_edge(CLK); + if (ENA_4 = '1') then + p2_h_t1 <= I_P2_H; + if (p2_h_t1 = '0') and (I_P2_H = '1') then + phase <= "11"; + else + phase <= phase + "1"; + end if; + end if; + end process; + + p_cs : process(I_CS1, I_CS2_L, I_P2_H) + begin + cs <= '0'; + if (I_CS1 = '1') and (I_CS2_L = '0') and (I_P2_H = '1') then + cs <= '1'; + end if; + end process; + + -- peripheral control reg (pcr) + -- 0 ca1 interrupt control (0 +ve edge, 1 -ve edge) + -- 3..1 ca2 operation + -- 000 input -ve edge + -- 001 independend interrupt input -ve edge + -- 010 input +ve edge + -- 011 independend interrupt input +ve edge + -- 100 handshake output + -- 101 pulse output + -- 110 low output + -- 111 high output + -- 7..4 as 3..0 for cb1,cb2 + + -- auxiliary control reg (acr) + -- 0 input latch PA (0 disable, 1 enable) + -- 1 input latch PB (0 disable, 1 enable) + -- 4..2 shift reg control + -- 000 disable + -- 001 shift in using t2 + -- 010 shift in using o2 + -- 011 shift in using ext clk + -- 100 shift out free running t2 rate + -- 101 shift out using t2 + -- 101 shift out using o2 + -- 101 shift out using ext clk + -- 5 t2 timer control (0 timed interrupt, 1 count down with pulses on pb6) + -- 7..6 t1 timer control + -- 00 timed interrupt each time t1 is loaded pb7 disable + -- 01 continuous interrupts pb7 disable + -- 00 timed interrupt each time t1 is loaded pb7 one shot output + -- 01 continuous interrupts pb7 square wave output + -- + + p_write_reg_reset : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + r_ora <= x"00"; r_orb <= x"00"; + r_ddra <= x"00"; r_ddrb <= x"00"; + r_acr <= x"00"; r_pcr <= x"00"; + + w_orb_hs <= '0'; + w_ora_hs <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + w_orb_hs <= '0'; + w_ora_hs <= '0'; + if (cs = '1') and (I_RW_L = '0') then + case I_RS is + when x"0" => r_orb <= I_DATA; w_orb_hs <= '1'; + when x"1" => r_ora <= I_DATA; w_ora_hs <= '1'; + when x"2" => r_ddrb <= I_DATA; + when x"3" => r_ddra <= I_DATA; + + when x"B" => r_acr <= I_DATA; + when x"C" => r_pcr <= I_DATA; + when x"F" => r_ora <= I_DATA; + + when others => null; + end case; + end if; + + if (r_acr(7) = '1') and (t1_toggle = '1') then + r_orb(7) <= not r_orb(7); -- toggle + end if; + end if; + end if; + end process; + + p_write_reg : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + t1_w_reset_int <= false; + t1_load_counter <= false; + + t2_w_reset_int <= false; + t2_load_counter <= false; + + load_data <= x"00"; + sr_write_ena <= false; + ifr_write_ena <= false; + ier_write_ena <= false; + + if (cs = '1') and (I_RW_L = '0') then + load_data <= I_DATA; + case I_RS is + when x"4" => r_t1l_l <= I_DATA; + when x"5" => r_t1l_h <= I_DATA; t1_w_reset_int <= true; + t1_load_counter <= true; + + when x"6" => r_t1l_l <= I_DATA; + when x"7" => r_t1l_h <= I_DATA; t1_w_reset_int <= true; + + when x"8" => r_t2l_l <= I_DATA; + when x"9" => r_t2l_h <= I_DATA; t2_w_reset_int <= true; + t2_load_counter <= true; + + when x"A" => sr_write_ena <= true; + when x"D" => ifr_write_ena <= true; + when x"E" => ier_write_ena <= true; + + when others => null; + end case; + end if; + end if; + end process; + + p_oe : process(cs, I_RW_L) + begin + O_DATA_OE_L <= '1'; + if (cs = '1') and (I_RW_L = '1') then + O_DATA_OE_L <= '0'; + end if; + end process; + + p_read : process(cs, I_RW_L, I_RS, r_irb, r_ira, r_ddrb, r_ddra, t1c, r_t1l_l, + r_t1l_h, t2c, r_sr, r_acr, r_pcr, r_ifr, r_ier, r_orb) + begin + t1_r_reset_int <= false; + t2_r_reset_int <= false; + sr_read_ena <= false; + r_irb_hs <= '0'; + r_ira_hs <= '0'; + O_DATA <= x"00"; -- default + if (cs = '1') and (I_RW_L = '1') then + case I_RS is + --when x"0" => O_DATA <= r_irb; r_irb_hs <= '1'; + -- fix from Mark McDougall, untested + when x"0" => O_DATA <= (r_irb and not r_ddrb) or (r_orb and r_ddrb); r_irb_hs <= '1'; + when x"1" => O_DATA <= r_ira; r_ira_hs <= '1'; + when x"2" => O_DATA <= r_ddrb; + when x"3" => O_DATA <= r_ddra; + when x"4" => O_DATA <= t1c( 7 downto 0); t1_r_reset_int <= true; + when x"5" => O_DATA <= t1c(15 downto 8); + when x"6" => O_DATA <= r_t1l_l; + when x"7" => O_DATA <= r_t1l_h; + when x"8" => O_DATA <= t2c( 7 downto 0); t2_r_reset_int <= true; + when x"9" => O_DATA <= t2c(15 downto 8); + when x"A" => O_DATA <= r_sr; sr_read_ena <= true; + when x"B" => O_DATA <= r_acr; + when x"C" => O_DATA <= r_pcr; + when x"D" => O_DATA <= r_ifr; + when x"E" => O_DATA <= ('0' & r_ier); + when x"F" => O_DATA <= r_ira; + when others => null; + end case; + end if; + + end process; + -- + -- IO + -- + p_ca1_cb1_sel : process(sr_cb1_oe_l, sr_cb1_out, I_CB1) + begin + -- if the shift register is enabled, cb1 may be an output + -- in this case, we should listen to the CB1_OUT for the interrupt + if (sr_cb1_oe_l = '1') then + cb1_in_mux <= I_CB1; + else + cb1_in_mux <= sr_cb1_out; + end if; + end process; + + p_ca1_cb1_int : process(r_pcr, ca1_ip_reg, I_CA1, cb1_ip_reg, cb1_in_mux) + begin + if (r_pcr(0) = '0') then -- ca1 control + -- negative edge + ca1_int <= (ca1_ip_reg = '1') and (I_CA1 = '0'); + else + -- positive edge + ca1_int <= (ca1_ip_reg = '0') and (I_CA1 = '1'); + end if; + + if (r_pcr(4) = '0') then -- cb1 control + -- negative edge + cb1_int <= (cb1_ip_reg = '1') and (cb1_in_mux = '0'); + else + -- positive edge + cb1_int <= (cb1_ip_reg = '0') and (cb1_in_mux = '1'); + end if; + end process; + + p_ca2_cb2_int : process(r_pcr, ca2_ip_reg, I_CA2, cb2_ip_reg, I_CB2) + begin + ca2_int <= false; + if (r_pcr(3) = '0') then -- ca2 input + if (r_pcr(2) = '0') then -- ca2 edge + -- negative edge + ca2_int <= (ca2_ip_reg = '1') and (I_CA2 = '0'); + else + -- positive edge + ca2_int <= (ca2_ip_reg = '0') and (I_CA2 = '1'); + end if; + end if; + + cb2_int <= false; + if (r_pcr(7) = '0') then -- cb2 input + if (r_pcr(6) = '0') then -- cb2 edge + -- negative edge + cb2_int <= (cb2_ip_reg = '1') and (I_CB2 = '0'); + else + -- positive edge + cb2_int <= (cb2_ip_reg = '0') and (I_CB2 = '1'); + end if; + end if; + end process; + + p_ca2_cb2 : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + O_CA2 <= '0'; + O_CA2_OE_L <= '1'; + O_CB2 <= '0'; + O_CB2_OE_L <= '1'; + + ca_hs_sr <= '0'; + ca_hs_pulse <= '0'; + cb_hs_sr <= '0'; + cb_hs_pulse <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- ca + if (phase = "00") and ((w_ora_hs = '1') or (r_ira_hs = '1')) then + ca_hs_sr <= '1'; + elsif ca1_int then + ca_hs_sr <= '0'; + end if; + + if (phase = "00") then + ca_hs_pulse <= w_ora_hs or r_ira_hs; + end if; + + O_CA2_OE_L <= not r_pcr(3); -- ca2 output + case r_pcr(3 downto 1) is + when "000" => O_CA2 <= '0'; -- input + when "001" => O_CA2 <= '0'; -- input + when "010" => O_CA2 <= '0'; -- input + when "011" => O_CA2 <= '0'; -- input + when "100" => O_CA2 <= not (ca_hs_sr); -- handshake + when "101" => O_CA2 <= not (ca_hs_pulse); -- pulse + when "110" => O_CA2 <= '0'; -- low + when "111" => O_CA2 <= '1'; -- high + when others => null; + end case; + + -- cb + if (phase = "00") and (w_orb_hs = '1') then + cb_hs_sr <= '1'; + elsif cb1_int then + cb_hs_sr <= '0'; + end if; + + if (phase = "00") then + cb_hs_pulse <= w_orb_hs; + end if; + + O_CB2_OE_L <= not (r_pcr(7) or sr_drive_cb2); -- cb2 output or serial + if (sr_drive_cb2 = '1') then -- serial output + O_CB2 <= sr_out; + else + case r_pcr(7 downto 5) is + when "000" => O_CB2 <= '0'; -- input + when "001" => O_CB2 <= '0'; -- input + when "010" => O_CB2 <= '0'; -- input + when "011" => O_CB2 <= '0'; -- input + when "100" => O_CB2 <= not (cb_hs_sr); -- handshake + when "101" => O_CB2 <= not (cb_hs_pulse); -- pulse + when "110" => O_CB2 <= '0'; -- low + when "111" => O_CB2 <= '1'; -- high + when others => null; + end case; + end if; + end if; + end if; + end process; + O_CB1 <= sr_cb1_out; + O_CB1_OE_L <= sr_cb1_oe_l; + + p_ca_cb_irq : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + ca1_irq <= '0'; + ca2_irq <= '0'; + cb1_irq <= '0'; + cb2_irq <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- not pretty + if ca1_int then + ca1_irq <= '1'; + elsif (r_ira_hs = '1') or (w_ora_hs = '1') or (clear_irq(1) = '1') then + ca1_irq <= '0'; + end if; + + if ca2_int then + ca2_irq <= '1'; + else + if (((r_ira_hs = '1') or (w_ora_hs = '1')) and (r_pcr(1) = '0')) or + (clear_irq(0) = '1') then + ca2_irq <= '0'; + end if; + end if; + + if cb1_int then + cb1_irq <= '1'; + elsif (r_irb_hs = '1') or (w_orb_hs = '1') or (clear_irq(4) = '1') then + cb1_irq <= '0'; + end if; + + if cb2_int then + cb2_irq <= '1'; + else + if (((r_irb_hs = '1') or (w_orb_hs = '1')) and (r_pcr(5) = '0')) or + (clear_irq(3) = '1') then + cb2_irq <= '0'; + end if; + end if; + end if; + end if; + end process; + + p_input_reg : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + ca1_ip_reg <= '0'; + cb1_ip_reg <= '0'; + + ca2_ip_reg <= '0'; + cb2_ip_reg <= '0'; + + r_ira <= x"00"; + r_irb <= x"00"; + + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- we have a fast clock, so we can have input registers + ca1_ip_reg <= I_CA1; + cb1_ip_reg <= cb1_in_mux; + + ca2_ip_reg <= I_CA2; + cb2_ip_reg <= I_CB2; + + if (r_acr(0) = '0') then + r_ira <= I_PA; + else -- enable latching + if ca1_int then + r_ira <= I_PA; + end if; + end if; + + if (r_acr(1) = '0') then + r_irb <= I_PB; + else -- enable latching + if cb1_int then + r_irb <= I_PB; + end if; + end if; + end if; + end if; + end process; + + + p_buffers : process(r_ddra, r_ora, r_ddrb, r_acr, r_orb) + begin + -- data direction reg (ddr) 0 = input, 1 = output + O_PA <= r_ora; + O_PA_OE_L <= not r_ddra; + + if (r_acr(7) = '1') then -- not clear if r_ddrb(7) must be 1 as well + O_PB_OE_L(7) <= '0'; -- an output if under t1 control + else + O_PB_OE_L(7) <= not (r_ddrb(7)); + end if; + + O_PB_OE_L(6 downto 0) <= not r_ddrb(6 downto 0); + O_PB(7 downto 0) <= r_orb(7 downto 0); + + end process; + -- + -- Timer 1 + -- + p_timer1_done : process + variable done : boolean; + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + done := (t1c = x"0000"); + t1c_done <= done and (phase = "11"); + if (phase = "11") then + t1_reload_counter <= done and (r_acr(6) = '1'); + end if; + end if; + end process; + + p_timer1 : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if t1_load_counter or (t1_reload_counter and phase = "11") then + t1c( 7 downto 0) <= r_t1l_l; + t1c(15 downto 8) <= r_t1l_h; + elsif (phase="11") then + t1c <= t1c - "1"; + end if; + + if t1_load_counter or t1_reload_counter then + t1c_active <= true; + elsif t1c_done then + t1c_active <= false; + end if; + + t1_toggle <= '0'; + if t1c_active and t1c_done then + t1_toggle <= '1'; + t1_irq <= '1'; + elsif t1_w_reset_int or t1_r_reset_int or (clear_irq(6) = '1') then + t1_irq <= '0'; + end if; + end if; + end process; + -- + -- Timer2 + -- + p_timer2_pb6_input : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if (phase = "01") then -- leading edge p2_h + t2_pb6 <= I_PB(6); + t2_pb6_t1 <= t2_pb6; + end if; + end if; + end process; + + p_timer2_done : process + variable done : boolean; + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + done := (t2c = x"0000"); + t2c_done <= done and (phase = "11"); + if (phase = "11") then + t2_reload_counter <= done; + end if; + end if; + end process; + + p_timer2 : process + variable ena : boolean; + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if (r_acr(5) = '0') then + ena := true; + else + ena := (t2_pb6_t1 = '1') and (t2_pb6 = '0'); -- falling edge + end if; + + if t2_load_counter or (t2_reload_counter and phase = "11") then + -- not sure if t2c_reload should be here. Does timer2 just continue to + -- count down, or is it reloaded ? Reloaded makes more sense if using + -- it to generate a clock for the shift register. + t2c( 7 downto 0) <= r_t2l_l; + t2c(15 downto 8) <= r_t2l_h; + else + if (phase="11") and ena then -- or count mode + t2c <= t2c - "1"; + end if; + end if; + + t2_sr_ena <= (t2c(7 downto 0) = x"00") and (phase = "11"); + + if t2_load_counter then + t2c_active <= true; + elsif t2c_done then + t2c_active <= false; + end if; + + + if t2c_active and t2c_done then + t2_irq <= '1'; + elsif t2_w_reset_int or t2_r_reset_int or (clear_irq(5) = '1') then + t2_irq <= '0'; + end if; + end if; + end process; + -- + -- Shift Register + -- + p_sr : process(RESET_L, CLK) + variable dir_out : std_logic; + variable ena : std_logic; + variable cb1_op : std_logic; + variable cb1_ip : std_logic; + variable use_t2 : std_logic; + variable free_run : std_logic; + variable sr_count_ena : boolean; + begin + if (RESET_L = '0') then + r_sr <= x"00"; + sr_drive_cb2 <= '0'; + sr_cb1_oe_l <= '1'; + sr_cb1_out <= '0'; + sr_strobe <= '1'; + sr_cnt <= "0000"; + sr_irq <= '0'; + sr_out <= '1'; + sr_off_delay <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- decode mode + dir_out := r_acr(4); -- output on cb2 + cb1_op := '0'; + cb1_ip := '0'; + use_t2 := '0'; + free_run := '0'; + + case r_acr(4 downto 2) is + when "000" => ena := '0'; + when "001" => ena := '1'; cb1_op := '1'; use_t2 := '1'; + when "010" => ena := '1'; cb1_op := '1'; + when "011" => ena := '1'; cb1_ip := '1'; + when "100" => ena := '1'; use_t2 := '1'; free_run := '1'; + when "101" => ena := '1'; cb1_op := '1'; use_t2 := '1'; + when "110" => ena := '1'; + when "111" => ena := '1'; cb1_ip := '1'; + when others => null; + end case; + + -- clock select + if (ena = '0') then + sr_strobe <= '1'; + else + if (cb1_ip = '1') then + sr_strobe <= I_CB1; + else + if (sr_cnt(3) = '0') and (free_run = '0') then + sr_strobe <= '1'; + else + if ((use_t2 = '1') and t2_sr_ena) or + ((use_t2 = '0') and (phase = "00")) then + sr_strobe <= not sr_strobe; + end if; + end if; + end if; + end if; + + -- latch on rising edge, shift on falling edge + if sr_write_ena then + r_sr <= load_data; + elsif (ena = '1') then -- use shift reg + + if (dir_out = '0') then + -- input + if (sr_cnt(3) = '1') or (cb1_ip = '1') then + if sr_strobe_rising then + r_sr(0) <= I_CB2; + elsif sr_strobe_falling then + r_sr(7 downto 1) <= r_sr(6 downto 0); + end if; + end if; + sr_out <= '1'; + else + -- output + if (sr_cnt(3) = '1') or (sr_off_delay = '1') or (cb1_ip = '1') or (free_run = '1') then + if sr_strobe_falling then + r_sr(7 downto 1) <= r_sr(6 downto 0); + r_sr(0) <= r_sr(7); + sr_out <= r_sr(7); + end if; + else + sr_out <= '1'; + end if; + end if; + end if; + + sr_count_ena := sr_strobe_rising; + + if sr_write_ena or sr_read_ena then + -- some documentation says sr bit in IFR must be set as well ? + sr_cnt <= "1000"; + elsif sr_count_ena and (sr_cnt(3) = '1') then + sr_cnt <= sr_cnt + "1"; + end if; + + if (phase = "00") then + sr_off_delay <= sr_cnt(3); -- give some hold time when shifting out + end if; + + if sr_count_ena and (sr_cnt = "1111") and (ena = '1') and (free_run = '0') then + sr_irq <= '1'; + elsif sr_write_ena or sr_read_ena or (clear_irq(2) = '1') then + sr_irq <= '0'; + end if; + + -- assign ops + sr_drive_cb2 <= dir_out; + sr_cb1_oe_l <= not cb1_op; + sr_cb1_out <= sr_strobe; + end if; + end if; + end process; + + p_sr_strobe_rise_fall : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + sr_strobe_t1 <= sr_strobe; + sr_strobe_rising <= (sr_strobe_t1 = '0') and (sr_strobe = '1'); + sr_strobe_falling <= (sr_strobe_t1 = '1') and (sr_strobe = '0'); + end if; + end process; + -- + -- Interrupts + -- + p_ier : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + r_ier <= "0000000"; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + if ier_write_ena then + if (load_data(7) = '1') then + -- set + r_ier <= r_ier or load_data(6 downto 0); + else + -- clear + r_ier <= r_ier and not load_data(6 downto 0); + end if; + end if; + end if; + end if; + end process; + + p_ifr : process(t1_irq, t2_irq, final_irq, ca1_irq, ca2_irq, sr_irq, + cb1_irq, cb2_irq) + begin + r_ifr(7) <= final_irq; + r_ifr(6) <= t1_irq; + r_ifr(5) <= t2_irq; + r_ifr(4) <= cb1_irq; + r_ifr(3) <= cb2_irq; + r_ifr(2) <= sr_irq; + r_ifr(1) <= ca1_irq; + r_ifr(0) <= ca2_irq; + + O_IRQ_L <= not final_irq; + end process; + + p_irq : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + final_irq <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + if ((r_ifr(6 downto 0) and r_ier(6 downto 0)) = "0000000") then + final_irq <= '0'; -- no interrupts + else + final_irq <= '1'; + end if; + end if; + end if; + end process; + + p_clear_irq : process(ifr_write_ena, load_data) + begin + clear_irq <= x"00"; + if ifr_write_ena then + clear_irq <= load_data; + end if; + end process; + +end architecture RTL; diff --git a/simple_uart.vhd b/simple_uart.vhd new file mode 100644 index 0000000..dc14416 --- /dev/null +++ b/simple_uart.vhd @@ -0,0 +1,233 @@ +-- Simple UART for debugging. Fixed baud rate, no handshaking or parity +-- +-- (C) 2011 Mike Stirling +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity simple_uart is +generic ( + f_clock : natural := 50000000; + baud_rate : natural := 115200 + ); +port ( + CLOCK : in std_logic; + nRESET : in std_logic; + + ENABLE : in std_logic; + -- Read not write + R_nW : in std_logic; + -- Data not status (address) + S_nD : in std_logic; + -- Data bus in + DI : in std_logic_vector(7 downto 0); + -- Data bus out + DO : out std_logic_vector(7 downto 0); + + -- Port pins + RXD : in std_logic; + TXD : out std_logic + ); +end entity; + +-- FIXME: RX really should have some mechanism for syncing with +-- the middle of the bit. Currently it is just pot luck! + +architecture rtl of simple_uart is +constant baud_const : natural := (f_clock / baud_rate) - 1; +subtype baud_div_t is natural range 0 to baud_const; +subtype bitcount_t is natural range 0 to 7; + +type state_t is (Idle, Sync, Start, Data, Stop); + +-- Clock enables +signal rx_clken : std_logic; +signal tx_clken : std_logic; +-- Baud rate divider for receive side +signal rx_baud_div : baud_div_t; +-- Baud rate divider for transmit side +signal tx_baud_div: baud_div_t; +-- Receive holding register +signal rx_reg : std_logic_vector(7 downto 0); +-- Receive holding register full flag +signal rx_ready : std_logic; +-- Receive shift register +signal rx_sreg : std_logic_vector(7 downto 0); +-- Receive state +signal rx_state : state_t; +-- Receive bit count +signal rx_bitcount : bitcount_t; +-- Latched rxd for falling edge detection +signal rxd_latch : std_logic; +-- Transmit input register +signal tx_reg : std_logic_vector(7 downto 0); +-- Transmit input register full flag +signal tx_busy : std_logic; +-- Transmit shift register +signal tx_sreg : std_logic_vector(7 downto 0); +-- Transmit state +signal tx_state : state_t; +-- Transmit bit count +signal tx_bitcount : bitcount_t; +-- Internal (async) representation of txd +signal txd_i : std_logic; +-- Latched enable for rising edge detection +signal enable_latch : std_logic; +begin + rx_clken <= '1' when rx_baud_div = 0 else '0'; + tx_clken <= '1' when tx_baud_div = 0 else '0'; + + txd_i <= + '0' when tx_state = Start else + tx_sreg(0) when tx_state = Data else + '1'; + + process(CLOCK,nRESET) + begin + if nRESET = '0' then + TXD <= '1'; + else + -- Register TXD to output + TXD <= txd_i; + end if; + end process; + + process(CLOCK,nRESET) + begin + if nRESET = '0' then + rx_reg <= (others => '0'); + rx_sreg <= (others => '0'); + rx_ready <= '0'; + rx_state <= Idle; + rx_bitcount <= 0; + + tx_reg <= (others => '0'); + tx_sreg <= (others => '0'); + tx_busy <= '0'; + tx_state <= Idle; + tx_bitcount <= 0; + + rx_baud_div <= baud_const; + tx_baud_div <= baud_const; + enable_latch <= '0'; + DO <= (others => '0'); + elsif rising_edge(CLOCK) then + -- Latch enable signal for edge detection + enable_latch <= ENABLE; + -- Latch rxd for falling edge (start) detection) + rxd_latch <= RXD; + + -- Baud rate generation. Tx and Rx are done separately so that + -- the receive counter can be adjusted when the falling edge of a + -- start bit is detected + if rx_baud_div = 0 then + -- Reload + rx_baud_div <= baud_const; + else + rx_baud_div <= rx_baud_div - 1; + end if; + if rx_state = Idle and RXD = '0' and rxd_latch = '1' then + -- Reset rx baud divider to mid point when a start bit occurs + rx_baud_div <= baud_const / 2; + end if; + if tx_baud_div = 0 then + -- Reload + tx_baud_div <= baud_const; + else + tx_baud_div <= tx_baud_div - 1; + end if; + + -- Handle bus writes - edge triggered to avoid unintentionally + -- re-transmitting the same character + if ENABLE = '1' and enable_latch = '0' and R_nW = '0' then + if S_nD = '0' and tx_busy = '0' then + -- Write to data register and tx register is empty + tx_reg <= DI; + tx_busy <= '1'; + end if; + -- All other writes are discarded + end if; + -- Handle bus reads - edge triggered so DO is held for the + -- duration of ENABLE, but a subsequent read with !rx_ready + -- will return 0 + if ENABLE = '1' and enable_latch = '0' and R_nW = '1' then + if S_nD = '0' then + -- Receive data register read + if rx_ready = '1' then + DO <= rx_reg; + rx_ready <= '0'; + else + DO <= (others => '0'); + end if; + else + -- Status register read + -- bit 0 = TX busy + -- bit 1 = RX ready + DO <= "000000" & rx_ready & tx_busy; + end if; + end if; + + if tx_busy = '1' and (tx_state = Idle or tx_state = Stop) then + -- Start a new transmit cycle. TXD remains idle until + -- the start of the next baud period, hence the additional + -- 'Sync' state. + tx_sreg <= tx_reg; + tx_busy <= '0'; + tx_state <= Sync; + tx_bitcount <= 7; + end if; + + -- Transmitter + if tx_clken = '1' and not (tx_state = Idle) then + case tx_state is + when Sync => + tx_state <= Start; + when Start => + tx_state <= Data; + when Data => + tx_sreg <= '0' & tx_sreg(7 downto 1); + if tx_bitcount = 0 then + tx_state <= Stop; + else + tx_bitcount <= tx_bitcount - 1; + end if; + when others => + -- Return to idle + tx_state <= Idle; + end case; + end if; + + -- Receiver + if rx_clken = '1' then + case rx_state is + when Idle => + if RXD = '0' then + -- Start bit detected - next bit is data, so we + -- skip the start state on the rx side + rx_state <= Data; + rx_bitcount <= 7; + end if; + when Data => + rx_sreg <= RXD & rx_sreg(7 downto 1); + if rx_bitcount = 0 then + rx_state <= Stop; + else + rx_bitcount <= rx_bitcount - 1; + end if; + when others => + -- Only latch output if stop bit matches and the + -- rx register is empty + if RXD = '1' and rx_ready = '0' then + rx_reg <= rx_sreg; + rx_ready <= '1'; + end if; + -- Return to idle + rx_state <= Idle; + end case; + end if; + end if; + end process; + +end architecture; -- cgit v1.2.3