aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Stirling <opensource@mikestirling.co.uk>2011-07-17 21:32:44 +0100
committerMike Stirling <opensource@mikestirling.co.uk>2011-07-17 21:32:44 +0100
commit78c1dae8212afdd81df8aebbece895f9bf2796b4 (patch)
treecd1026289ddc9c32b8c127e8dcfb4bb7dba13127
parent3eee5c973d8373cf0735a6af1d818440b56dd4e2 (diff)
downloadfpga-bbc-78c1dae8212afdd81df8aebbece895f9bf2796b4.tar.gz
fpga-bbc-78c1dae8212afdd81df8aebbece895f9bf2796b4.tar.bz2
fpga-bbc-78c1dae8212afdd81df8aebbece895f9bf2796b4.zip
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
-rw-r--r--bbc_micro_de1.qsf11
-rw-r--r--bbc_micro_de1.vhd327
-rw-r--r--ehbasic.qip3
-rw-r--r--ehbasic.vhd168
-rw-r--r--m6522.vhd889
-rw-r--r--simple_uart.vhd233
6 files changed, 1613 insertions, 18 deletions
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;