]> git.the-white-hart.net Git - vhdl/commitdiff
Initial commit
authorrs <>
Sat, 28 Jun 2025 03:19:34 +0000 (22:19 -0500)
committerrs <>
Sat, 28 Jun 2025 03:19:34 +0000 (22:19 -0500)
77 files changed:
.gitignore [new file with mode: 0644]
libraries/nexys2/epp_wb.vhd [new file with mode: 0644]
libraries/nexys2/host_ctrl.vhd [new file with mode: 0644]
libraries/nexys2/host_regs.vhd [new file with mode: 0644]
libraries/nexys2/mem_mux.vhd [new file with mode: 0644]
libraries/nexys2/mem_wb8_0.vhd [new file with mode: 0644]
libraries/nexys2/mem_wb_basic.vhd [new file with mode: 0644]
libraries/nexys2/seven_seg_hex.vhd [new file with mode: 0644]
libraries/nexys2/seven_seg_mux.vhd [new file with mode: 0644]
libraries/nexys2/tests/program.sh [new file with mode: 0755]
libraries/nexys2/tests/test_nexys2_mem_wb8_0.vhd [new file with mode: 0644]
libraries/nexys2/tests/test_sim_mem_wb8_0.vhd [new file with mode: 0644]
libraries/nexys2/wishbone_debugger.vhd [new file with mode: 0644]
libraries/ps2/ps2_host.vhd [new file with mode: 0644]
libraries/ps2/ps2_host_wb.vhd [new file with mode: 0644]
libraries/ps2/tests/nexys2.vhd [new file with mode: 0644]
libraries/ps2/tests/program.sh [new file with mode: 0755]
libraries/ps2/tests/test_ps2_host.vhd [new file with mode: 0644]
libraries/ps2/tests/test_ps2_host_wb.vhd [new file with mode: 0644]
libraries/rs232/rs232_rx.vhd [new file with mode: 0644]
libraries/rs232/rs232_tx.vhd [new file with mode: 0644]
libraries/rs232/rs232_uart.vhd [new file with mode: 0644]
libraries/rs232/tests/program.sh [new file with mode: 0755]
libraries/rs232/tests/test_nexys2.vhd [new file with mode: 0644]
libraries/rs232/tests/test_rs232_rx.vhd [new file with mode: 0644]
libraries/rs232/tests/test_rs232_tx.vhd [new file with mode: 0644]
libraries/rs232/tests/test_uart.vhd [new file with mode: 0644]
libraries/simulated/delay_edges.vhd [new file with mode: 0644]
libraries/simulated/is_unstable.vhd [new file with mode: 0644]
libraries/simulated/is_unstable_v.vhd [new file with mode: 0644]
libraries/simulated/js28f128j3d75.vhd [new file with mode: 0644]
libraries/simulated/mt45w8mw16bgx.vhd [new file with mode: 0644]
libraries/simulated/ps2_device.vhd [new file with mode: 0644]
libraries/simulated/tests/test_js28f128j3d75.vhd [new file with mode: 0644]
libraries/simulated/tests/test_mt45w8mw16bgx.vhd [new file with mode: 0644]
libraries/simulated/tests/test_ps2_device.vhd [new file with mode: 0644]
libraries/simulated/tests/test_simmem.vhd [new file with mode: 0644]
libraries/simulated/wb_memory.vhd [new file with mode: 0644]
libraries/utility/debounce.vhd [new file with mode: 0644]
libraries/utility/fifo.vhd [new file with mode: 0644]
libraries/utility/fifo16.vhd [new file with mode: 0644]
libraries/utility/perf_counter.vhd [new file with mode: 0644]
libraries/utility/power_on_reset.vhd [new file with mode: 0644]
libraries/utility/tests/test_fifo.vhd [new file with mode: 0644]
libraries/utility/tests/test_mapper.vhd [new file with mode: 0644]
libraries/utility/wb_debug.vhd [new file with mode: 0644]
libraries/utility/wb_mapper.vhd [new file with mode: 0644]
libraries/utility/wb_mux2.vhd [new file with mode: 0644]
libraries/utility/wb_mux4.vhd [new file with mode: 0644]
libraries/vga/tests/program.sh [new file with mode: 0755]
libraries/vga/tests/test_nexys2.vhd [new file with mode: 0644]
libraries/vga/vga_counter.vhd [new file with mode: 0644]
libraries/vga/vga_tiler.vhd [new file with mode: 0644]
projects/cpu_0/asm/as.py [new file with mode: 0755]
projects/cpu_0/asm/hellorld.asm [new file with mode: 0644]
projects/cpu_0/asm/test.asm [new file with mode: 0644]
projects/cpu_0/cpu.vhd [new file with mode: 0644]
projects/cpu_0/int_ctrl.vhd [new file with mode: 0644]
projects/cpu_0/nexys2.vhd [new file with mode: 0644]
projects/cpu_0/program.sh [new file with mode: 0755]
projects/cpu_0/tests/test_cpu.vhd [new file with mode: 0644]
projects/cpu_0/tests/test_intctrl.vhd [new file with mode: 0644]
projects/cpu_0/tests/test_nexys2.vhd [new file with mode: 0644]
projects/nexys2_host_controller/device/program.sh [new file with mode: 0755]
projects/nexys2_host_controller/host/build.sh [new file with mode: 0755]
projects/nexys2_host_controller/host/digdude.c [new file with mode: 0644]
projects/nexys2_host_controller/host/ec.c [new file with mode: 0644]
projects/nexys2_host_controller/host/ec.h [new file with mode: 0644]
projects/nexys2_host_controller/host/epp_read.c [new file with mode: 0644]
projects/nexys2_host_controller/host/epp_write.c [new file with mode: 0644]
projects/nexys2_host_controller/host/fileformat.c [new file with mode: 0644]
projects/nexys2_host_controller/host/fileformat.h [new file with mode: 0644]
projects/nexys2_host_controller/host/memctrl.c [new file with mode: 0644]
projects/nexys2_host_controller/host/memctrl.h [new file with mode: 0644]
projects/nexys2_host_controller/host/test.sh [new file with mode: 0755]
projects/nexys2_host_controller/nexys2.vhd [new file with mode: 0644]
projects/nexys2_host_controller/testbench_hostctrl.vhd [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..1598bd3
--- /dev/null
@@ -0,0 +1,10 @@
+*.bit
+*.bin
+
+# Xilinx ISE license file
+*.lic
+
+# The UCF files for the Nexys2 come from Digilent, and aren't mine to distribute
+*.ucf
+
+.idea/
diff --git a/libraries/nexys2/epp_wb.vhd b/libraries/nexys2/epp_wb.vhd
new file mode 100644 (file)
index 0000000..a854496
--- /dev/null
@@ -0,0 +1,192 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity epp_wb is
+       port (
+               -- Epp-bus signals
+               EppAstb: in    std_logic;
+               EppDstb: in    std_logic;
+               EppWr:   in    std_logic;
+               EppDB:   inout std_logic_vector(7 downto 0);
+               EppWait: out   std_logic;
+
+               -- Wishbone bus signals
+               rst_i:   in    std_logic;
+               clk_i:   in    std_logic;
+               adr_o:   out   std_logic_vector(7 downto 0);
+               dat_i:   in    std_logic_vector(7 downto 0);
+               dat_o:   out   std_logic_vector(7 downto 0);
+               cyc_o:   out   std_logic;
+               stb_o:   out   std_logic;
+               we_o:    out   std_logic;
+               ack_i:   in    std_logic
+       );
+end epp_wb;
+
+
+architecture behavioral of epp_wb is
+
+       type state_t is (
+               S_READY,
+               S_ADDR,
+               S_DATA,
+               S_DONE
+       );
+
+       -- Registered epp bus signals
+       signal epp_astb_reg:       std_logic;
+       signal epp_dstb_reg:       std_logic;
+       signal epp_wr_reg:         std_logic;
+       signal epp_data_in_reg:    std_logic_vector(7 downto 0);
+       signal epp_data_out_reg:   std_logic_vector(7 downto 0);
+
+       signal epp_data_out_latch: std_logic;
+       signal epp_data_out_en:    std_logic;
+
+       -- Internal address register
+       signal addr_latch: std_logic;
+       signal addr_reg: std_logic_vector(7 downto 0);
+
+       -- Registered wishbone bus signals
+       signal next_stb: std_logic;
+       signal stb_reg:  std_logic;
+       signal next_we:  std_logic;
+       signal we_reg:   std_logic;
+
+       -- State machine
+       signal cur_state_reg: state_t;
+       signal next_state: state_t;
+
+begin
+
+       -- Interface output should be in tristate until data is available for EPP to read
+       EppDB <= epp_data_out_reg when epp_data_out_en = '1' else (others => 'Z');
+
+       -- Output registered wishbone signals
+       cyc_o <= stb_reg;
+       stb_o <= stb_reg;
+       we_o  <= we_reg;
+
+       -- Always present the latched EPP data bus and latched address to the wishbone bus
+       dat_o <= epp_data_in_reg;
+       adr_o <= addr_reg;
+
+       -- State register
+       p_regs: process (clk_i, rst_i, next_state)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               cur_state_reg <= S_READY;
+                       else
+                               cur_state_reg <= next_state;
+                       end if;
+               end if;
+       end process;
+
+       -- Regiter EPP bus inputs to break combinational chain with the outside world
+       p_epp_reg: process (clk_i, EppAstb, EppDstb, EppWr, EppDB)
+       begin
+               if rising_edge(clk_i) then
+                       epp_astb_reg    <= EppAstb;
+                       epp_dstb_reg    <= EppDstb;
+                       epp_wr_reg      <= EppWr;
+                       epp_data_in_reg <= EppDB;
+               end if;
+       end process;
+
+       -- Register wishbone outputs
+       p_wb_reg: process (clk_i, rst_i, next_stb, next_we)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               stb_reg <= '0';
+                               we_reg  <= '0';
+                       else
+                               stb_reg <= next_stb;
+                               we_reg  <= next_we;
+                       end if;
+               end if;
+       end process;
+
+       -- Address register
+       p_addr: process (clk_i, addr_latch, epp_data_in_reg)
+       begin
+               if rising_edge(clk_i) and addr_latch = '1' then
+                       addr_reg <= epp_data_in_reg;
+               end if;
+       end process;
+
+       -- Data register from wishbone bus to epp bus
+       p_data: process (clk_i, epp_data_out_latch, dat_i)
+       begin
+               if rising_edge(clk_i) and epp_data_out_latch = '1' then
+                       epp_data_out_reg <= dat_i;
+               end if;
+       end process;
+
+       -- State machine transitions
+       p_fsm: process(cur_state_reg,
+                      epp_astb_reg, epp_dstb_reg, epp_wr_reg,
+                      ack_i)
+       begin
+               -- Default to remaining in same state
+               next_state <= cur_state_reg;
+
+               -- Default to not starting a wishbone cycle
+               next_stb <= '0';
+               next_we  <= '0';
+
+               -- Default to not latching or outputting anything
+               addr_latch <= '0';
+               epp_data_out_latch <= '0';
+               epp_data_out_en <= '0';
+
+               -- Default to telling EPP-side to wait
+               EppWait <= '0';
+
+               case cur_state_reg is
+                       when S_READY =>
+                               -- Leave state when a strobe is asserted
+                               if epp_astb_reg = '0' then
+                                       next_state <= S_ADDR;
+                               elsif epp_dstb_reg = '0' then
+                                       next_stb <= '1';
+                                       next_we <= not epp_wr_reg;
+                                       next_state <= S_DATA;
+                               end if;
+
+                       when S_DATA =>
+                               -- Ack may be combinational, so don't register it
+                               if ack_i = '1' then
+                                       -- Latch data from wishbone, as EPP may take more time to read
+                                       epp_data_out_latch <= '1';
+                                       next_state <= S_DONE;
+                               else
+                                       -- Continue asserting bus signals next cycle if not acknowledged
+                                       next_stb <= '1';
+                                       next_we <= not epp_wr_reg;
+                               end if;
+
+                       when S_ADDR =>
+                               addr_latch <= '1';
+                               next_state <= S_DONE;  -- No stretcing will occur for address access
+
+                       when S_DONE =>
+                               -- Indicate completion
+                               EppWait <= '1';
+
+                               -- If a read is being performed, take the EPP bus out of tristate
+                               epp_data_out_en <= epp_wr_reg;
+
+                               -- Wait for strobes to be deasserted before returning to READY state
+                               if epp_astb_reg = '1' and epp_dstb_reg = '1' then
+                                       next_state <= S_READY;
+                               end if;
+
+                       when others =>
+                               next_state <= S_READY;
+               end case;
+       end process;
+
+end behavioral;
diff --git a/libraries/nexys2/host_ctrl.vhd b/libraries/nexys2/host_ctrl.vhd
new file mode 100644 (file)
index 0000000..5839450
--- /dev/null
@@ -0,0 +1,326 @@
+--------------------------------------------------------------------------------
+-- Registers:
+--
+-- 0: CTRL
+-- 1: MBOX_FLAGS
+-- 2: MBOX_VALUE
+-- 3: SW(R)/LED(W)
+-- 4: SSEG0
+-- 5: SSEG1
+-- 6: SSEG2
+-- 7: SSEG3
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library utility;
+library work;
+
+
+entity host_ctrl is
+       port (
+               -- Wishbone global signals
+               clk_i:        in    std_logic;
+               rst_i:        in    std_logic;
+
+               -- Signals to device
+               d_rst_o:      out   std_logic;
+               d_flags_o:    out   std_logic_vector(7 downto 0);
+               debug_i:      in    std_logic_vector(63 downto 0);
+               debug_o:      out   std_logic_vector(63 downto 0);
+
+               -- Device-side wishbone bus slave
+               d_cyc_i:      in    std_logic;
+               d_stb_i:      in    std_logic;
+               d_we_i:       in    std_logic;
+               d_ack_o:      out   std_logic;
+               d_adr_i:      in    std_logic_vector(2 downto 0);
+               d_dat_i:      in    std_logic_vector(7 downto 0);
+               d_dat_o:      out   std_logic_vector(7 downto 0);
+
+               -- Device memory interface
+               d_MemOE:      in    std_logic;
+               d_MemWR:      in    std_logic;
+               d_RamAdv:     in    std_logic;
+               d_RamCS:      in    std_logic;
+               d_RamClk:     in    std_logic;
+               d_RamCRE:     in    std_logic;
+               d_RamLB:      in    std_logic;
+               d_RamUB:      in    std_logic;
+               d_RamWait:    out   std_logic;
+               d_FlashRp:    in    std_logic;
+               d_FlashCS:    in    std_logic;
+               d_FlashStSts: out   std_logic;
+               d_MemAdr:     in    std_logic_vector(23 downto 1);
+               d_MemDB_o:    in    std_logic_vector(15 downto 0);  -- outbound - from device to memory
+               d_MemDB_i:    out   std_logic_vector(15 downto 0);  -- inbound - from memory to device
+
+               -- Nexys2 IO pins
+               EppAstb:      in    std_logic;
+               EppDstb:      in    std_logic;
+               EppWr:        in    std_logic;
+               EppDB:        inout std_logic_vector(7 downto 0);
+               EppWait:      out   std_logic;
+
+               MemOE:        out   std_logic;
+               MemWR:        out   std_logic;
+               RamAdv:       out   std_logic;
+               RamCS:        out   std_logic;
+               RamClk:       out   std_logic;
+               RamCRE:       out   std_logic;
+               RamLB:        out   std_logic;
+               RamUB:        out   std_logic;
+               RamWait:      in    std_logic;
+               FlashRp:      out   std_logic;
+               FlashCS:      out   std_logic;
+               FlashStSts:   in    std_logic;
+               MemAdr:       out   std_logic_vector(23 downto 1);
+               MemDB:        inout std_logic_vector(15 downto 0);
+
+               seg:          out   std_logic_vector(6 downto 0);
+               dp:           out   std_logic;
+               an:           out   std_logic_vector(3 downto 0);
+               Led:          out   std_logic_vector(7 downto 0);
+               sw:           in    std_logic_vector(7 downto 0)
+       );
+end host_ctrl;
+
+
+architecture behavioral of host_ctrl is
+
+       -- Host Wishbone bus from EPP bridge
+       signal h_cyc:           std_logic;
+       signal h_stb:           std_logic;
+       signal h_we:            std_logic;
+       signal h_ack:           std_logic;
+       signal h_adr:           std_logic_vector(7 downto 0);
+       signal h_dat_miso:      std_logic_vector(7 downto 0);
+       signal h_dat_mosi:      std_logic_vector(7 downto 0);
+
+       signal h_cyc_ctl:       std_logic;
+       signal h_dat_miso_ctl:  std_logic_vector(7 downto 0);
+       signal h_ack_ctl:       std_logic;
+
+       signal h_cyc_oob:       std_logic;
+       signal h_dat_miso_oob:  std_logic_vector(7 downto 0);
+       signal h_ack_oob:       std_logic;
+
+       signal h_cyc_deb:       std_logic;
+       signal h_dat_miso_deb:  std_logic_vector(7 downto 0);
+       signal h_ack_deb:       std_logic;
+
+       -- Control register signals
+       signal ctrl:            std_logic_vector(7 downto 0);
+       signal flags:           std_logic_vector(7 downto 0);
+
+       -- Out-of-band memory controller signals
+       signal oob_mem_oe:      std_logic;
+       signal oob_mem_wr:      std_logic;
+       signal oob_ram_adv:     std_logic;
+       signal oob_ram_cs:      std_logic;
+       signal oob_ram_clk:     std_logic;
+       signal oob_ram_cre:     std_logic;
+       signal oob_ram_ub:      std_logic;
+       signal oob_ram_lb:      std_logic;
+       signal oob_ram_wait:    std_logic;
+       signal oob_flash_rp:    std_logic;
+       signal oob_flash_cs:    std_logic;
+       signal oob_flash_ststs: std_logic;
+       signal oob_mem_adr:     std_logic_vector(23 downto 1);
+       signal oob_mem_db_i:    std_logic_vector(15 downto 0);
+       signal oob_mem_db_o:    std_logic_vector(15 downto 0);
+
+begin
+
+       -- EPP to Wishbone bridge
+       e_epp_wb: entity work.epp_wb
+               port map (
+                       EppAstb => EppAstb,
+                       EppDstb => EppDstb,
+                       EppWr   => EppWr,
+                       EppDB   => EppDB,
+                       EppWait => EppWait,
+
+                       rst_i   => rst_i,
+                       clk_i   => clk_i,
+                       adr_o   => h_adr,
+                       dat_i   => h_dat_miso,
+                       dat_o   => h_dat_mosi,
+                       cyc_o   => h_cyc,
+                       stb_o   => h_stb,
+                       we_o    => h_we,
+                       ack_i   => h_ack
+               );
+
+
+       -- Host-side Wishbone multiplexer
+       p_wb_mux: process (h_adr, h_cyc,
+                          h_dat_miso_ctl, h_ack_ctl,
+                          h_dat_miso_oob, h_ack_oob)
+       begin
+               h_cyc_ctl <= '0';
+               h_cyc_oob <= '0';
+
+               case h_adr(7 downto 3) is
+                       when "00000" =>
+                               h_cyc_ctl  <= h_cyc;
+                               h_dat_miso <= h_dat_miso_ctl;
+                               h_ack      <= h_ack_ctl;
+
+                       when "00001" =>
+                               h_cyc_oob  <= h_cyc;
+                               h_dat_miso <= h_dat_miso_oob;
+                               h_ack      <= h_ack_oob;
+
+                       when "00010" =>
+                               h_cyc_deb  <= h_cyc;
+                               h_dat_miso <= h_dat_miso_deb;
+                               h_ack      <= h_ack_deb;
+
+                       when others =>
+                               h_dat_miso <= (others => '1');
+                               h_ack      <= '1';  -- Eat to prevent deadlocks
+               end case;
+       end process;
+
+
+       -- Wishbone slave for control registers
+       e_host_regs: entity work.host_regs
+               port map (
+                       rst_i   => rst_i,
+                       clk_i   => clk_i,
+
+                       h_cyc_i => h_cyc_ctl,
+                       h_stb_i => h_stb,
+                       h_we_i  => h_we,
+                       h_ack_o => h_ack_ctl,
+                       h_adr_i => h_adr(2 downto 0),
+                       h_dat_i => h_dat_mosi,
+                       h_dat_o => h_dat_miso_ctl,
+
+                       d_cyc_i => d_cyc_i,
+                       d_stb_i => d_stb_i,
+                       d_we_i  => d_we_i,
+                       d_ack_o => d_ack_o,
+                       d_adr_i => d_adr_i,
+                       d_dat_i => d_dat_i,
+                       d_dat_o => d_dat_o,
+
+                       ctrl    => ctrl,
+                       flags   => d_flags_o,
+                       seg     => seg,
+                       dp      => dp,
+                       an      => an,
+                       Led     => Led,
+                       sw      => sw
+               );
+
+       -- Auxiliary signals to device
+       d_rst_o  <= ctrl(7);
+
+
+       -- Wishbone slave for out-of-band memory controller
+       e_mem_oob: entity work.mem_wb_basic
+               port map (
+                       clk_i      => clk_i,
+                       rst_i      => rst_i,
+
+                       cyc_i      => h_cyc_oob,
+                       stb_i      => h_stb,
+                       we_i       => h_we,
+                       ack_o      => h_ack_oob,
+                       adr_i      => h_adr(2 downto 0),
+                       dat_i      => h_dat_mosi,
+                       dat_o      => h_dat_miso_oob,
+
+                       MemOE      => oob_mem_oe,
+                       MemWR      => oob_mem_wr,
+                       RamAdv     => oob_ram_adv,
+                       RamCS      => oob_ram_cs,
+                       RamClk     => oob_ram_clk,
+                       RamCRE     => oob_ram_cre,
+                       RamUB      => oob_ram_ub,
+                       RamLB      => oob_ram_lb,
+                       RamWait    => oob_ram_wait,
+                       FlashRp    => oob_flash_rp,
+                       FlashCS    => oob_flash_cs,
+                       FlashStSts => oob_flash_ststs,
+                       MemAdr     => oob_mem_adr,
+                       MemDB_i    => oob_mem_db_i,
+                       MemDB_o    => oob_mem_db_o
+               );
+
+
+       -- Wishbone slave for debug reads
+       e_debug: entity utility.wb_debug
+               port map (
+                       rst_i   => rst_i,
+                       clk_i   => clk_i,
+                       cyc_i   => h_cyc_deb,
+                       stb_i   => h_stb,
+                       we_i    => h_we,
+                       ack_o   => h_ack_deb,
+                       adr_i   => h_adr(2 downto 0),
+                       dat_i   => h_dat_mosi,
+                       dat_o   => h_dat_miso_deb,
+                       debug_i => debug_i,
+                       debug_o => debug_o
+               );
+
+
+       -- Memory multiplexer
+       e_mem_mux: entity work.mem_mux
+               port map (
+                       sel           => ctrl(6),
+
+                       s0_MemOE      => d_MemOE,
+                       s0_MemWR      => d_MemWR,
+                       s0_RamAdv     => d_RamAdv,
+                       s0_RamCS      => d_RamCS,
+                       s0_RamClk     => d_RamClk,
+                       s0_RamCRE     => d_RamCRE,
+                       s0_RamUB      => d_RamUB,
+                       s0_RamLB      => d_RamLB,
+                       s0_RamWait    => d_RamWait,
+                       s0_FlashRp    => d_FlashRp,
+                       s0_FlashCS    => d_FlashCS,
+                       s0_FlashStSts => d_FlashStSts,
+                       s0_MemAdr     => d_MemAdr,
+                       s0_MemDB_i    => d_MemDB_i,
+                       s0_MemDB_o    => d_MemDB_o,
+
+                       s1_MemOE      => oob_mem_oe,
+                       s1_MemWR      => oob_mem_wr,
+                       s1_RamAdv     => oob_ram_adv,
+                       s1_RamCS      => oob_ram_cs,
+                       s1_RamClk     => oob_ram_clk,
+                       s1_RamCRE     => oob_ram_cre,
+                       s1_RamUB      => oob_ram_ub,
+                       s1_RamLB      => oob_ram_lb,
+                       s1_RamWait    => oob_ram_wait,
+                       s1_FlashRp    => oob_flash_rp,
+                       s1_FlashCS    => oob_flash_cs,
+                       s1_FlashStSts => oob_flash_ststs,
+                       s1_MemAdr     => oob_mem_adr,
+                       s1_MemDB_i    => oob_mem_db_i,
+                       s1_MemDB_o    => oob_mem_db_o,
+
+                       MemOE         => MemOE,
+                       MemWR         => MemWR,
+                       RamAdv        => RamAdv,
+                       RamCS         => RamCS,
+                       RamClk        => RamClk,
+                       RamCRE        => RamCRE,
+                       RamUB         => RamUB,
+                       RamLB         => RamLB,
+                       RamWait       => RamWait,
+                       FlashRp       => FlashRp,
+                       FlashCS       => FlashCS,
+                       FlashStSts    => FlashStSts,
+                       MemAdr        => MemAdr,
+                       MemDB         => MemDB
+               );
+
+end behavioral;
diff --git a/libraries/nexys2/host_regs.vhd b/libraries/nexys2/host_regs.vhd
new file mode 100644 (file)
index 0000000..8ea6e6d
--- /dev/null
@@ -0,0 +1,128 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+
+
+entity host_regs is
+       port (
+               rst_i:   in  std_logic;
+               clk_i:   in  std_logic;
+
+               h_cyc_i: in  std_logic;
+               h_stb_i: in  std_logic;
+               h_we_i:  in  std_logic;
+               h_ack_o: out std_logic;
+               h_adr_i: in  std_logic_vector(2 downto 0);
+               h_dat_i: in  std_logic_vector(7 downto 0);
+               h_dat_o: out std_logic_vector(7 downto 0);
+
+               d_cyc_i: in  std_logic;
+               d_stb_i: in  std_logic;
+               d_we_i:  in  std_logic;
+               d_ack_o: out std_logic;
+               d_adr_i: in  std_logic_vector(2 downto 0);
+               d_dat_i: in  std_logic_vector(7 downto 0);
+               d_dat_o: out std_logic_vector(7 downto 0);
+
+               ctrl:    out std_logic_vector(7 downto 0);
+               flags:   out std_logic_vector(7 downto 0);
+               seg:     out std_logic_vector(6 downto 0);
+               dp:      out std_logic;
+               an:      out std_logic_vector(3 downto 0);
+               Led:     out std_logic_vector(7 downto 0);
+               sw:      in  std_logic_vector(7 downto 0)
+       );
+end host_regs;
+
+
+architecture behavioral of host_regs is
+
+       type byte_array is array(natural range <>) of std_logic_vector(7 downto 0);
+
+       signal regs: byte_array(7 downto 0);
+
+       signal clk_div: unsigned(15 downto 0);
+       signal temp_an: std_logic_vector(3 downto 0);
+
+begin
+
+       h_ack_o <= '1';
+       h_dat_o <= sw when h_adr_i = "011"
+                     else regs(to_integer(unsigned(h_adr_i)));
+
+       d_ack_o <= '0' when h_cyc_i = '1' and h_we_i = '1' else '1';
+       d_dat_o <= sw when d_adr_i = "011"
+                     else regs(to_integer(unsigned(d_adr_i)));
+
+       process (clk_i, rst_i,
+                h_cyc_i, h_stb_i, h_we_i, h_adr_i, h_dat_i,
+                d_cyc_i, d_stb_i, d_we_i, d_adr_i, d_dat_i,
+                sw)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               -- Reset
+                               regs(0) <= (7 => '1', others => '0');
+                               regs(1) <= (others => '0');
+                               regs(2) <= (others => '0');
+                               regs(3) <= (others => '0');
+                               regs(4) <= (others => '1');
+                               regs(5) <= (others => '1');
+                               regs(6) <= (others => '1');
+                               regs(7) <= (others => '1');
+                       else
+                               if h_cyc_i = '1' and h_stb_i = '1' then
+                                       if h_we_i = '1' then
+                                               -- Host write
+                                               case h_adr_i is
+                                                       when "001"  => regs(1) <= regs(1) or h_dat_i;  -- Register 1 is write-one-set
+                                                       when others => regs(to_integer(unsigned(h_adr_i))) <= h_dat_i;
+                                               end case;
+                                       end if;
+                               elsif d_cyc_i = '1' and d_stb_i = '1' then
+                                       if d_we_i = '1' then
+                                               -- Device write
+                                               case d_adr_i(2 downto 0) is
+                                                       when "000"  => null;  -- Register 0 is read-only
+                                                       when "001"  => regs(1) <= regs(1) and not d_dat_i;  -- Register 1 is write-one-clear
+                                                       when others => regs(to_integer(unsigned(d_adr_i))) <= d_dat_i;
+                                               end case;
+                                       end if;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       ctrl <= regs(0);
+       flags <= regs(1);
+
+       -- LEDs
+       Led <= regs(3);
+
+       process (clk_i, clk_div)
+       begin
+               if rising_edge(clk_i) then
+                       clk_div <= clk_div + 1;
+               end if;
+       end process;
+
+       temp_an <= regs(7)(7) & regs(6)(7) & regs(5)(7) & regs(4)(7);
+
+       e_seven_seg_mux: entity work.seven_seg_mux
+               port map (
+                       clk_in   => clk_div(15),
+
+                       seg_0_in => regs(4)(6 downto 0),
+                       seg_1_in => regs(5)(6 downto 0),
+                       seg_2_in => regs(6)(6 downto 0),
+                       seg_3_in => regs(7)(6 downto 0),
+                       dps_in   => temp_an,
+
+                       seg_out  => seg,
+                       dp_out   => dp,
+                       an_out   => an
+               );
+
+end behavioral;
diff --git a/libraries/nexys2/mem_mux.vhd b/libraries/nexys2/mem_mux.vhd
new file mode 100644 (file)
index 0000000..acde130
--- /dev/null
@@ -0,0 +1,91 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity mem_mux is
+       port (
+               sel:           in    std_logic;
+
+               s0_MemOE:      in    std_logic;
+               s0_MemWR:      in    std_logic;
+               s0_RamAdv:     in    std_logic;
+               s0_RamCS:      in    std_logic;
+               s0_RamClk:     in    std_logic;
+               s0_RamCRE:     in    std_logic;
+               s0_RamUB:      in    std_logic;
+               s0_RamLB:      in    std_logic;
+               s0_RamWait:    out   std_logic;
+               s0_FlashRp:    in    std_logic;
+               s0_FlashCS:    in    std_logic;
+               s0_FlashStSts: out   std_logic;
+               s0_MemAdr:     in    std_logic_vector(23 downto 1);
+               s0_MemDB_i:    out   std_logic_vector(15 downto 0);  -- Inbound: from memory to device
+               s0_MemDB_o:    in    std_logic_vector(15 downto 0);  -- Outbound: from device to memory
+
+               s1_MemOE:      in    std_logic;
+               s1_MemWR:      in    std_logic;
+               s1_RamAdv:     in    std_logic;
+               s1_RamCS:      in    std_logic;
+               s1_RamClk:     in    std_logic;
+               s1_RamCRE:     in    std_logic;
+               s1_RamUB:      in    std_logic;
+               s1_RamLB:      in    std_logic;
+               s1_RamWait:    out   std_logic;
+               s1_FlashRp:    in    std_logic;
+               s1_FlashCS:    in    std_logic;
+               s1_FlashStSts: out   std_logic;
+               s1_MemAdr:     in    std_logic_vector(23 downto 1);
+               s1_MemDB_i:    out   std_logic_vector(15 downto 0);  -- Inbound: from memory to device
+               s1_MemDB_o:    in    std_logic_vector(15 downto 0);  -- Outbound: from device to memory
+
+               MemOE:         out   std_logic;
+               MemWR:         out   std_logic;
+               RamAdv:        out   std_logic;
+               RamCS:         out   std_logic;
+               RamClk:        out   std_logic;
+               RamCRE:        out   std_logic;
+               RamUB:         out   std_logic;
+               RamLB:         out   std_logic;
+               RamWait:       in    std_logic;
+               FlashRp:       out   std_logic;
+               FlashCS:       out   std_logic;
+               FlashStSts:    in    std_logic;
+               MemAdr:        out   std_logic_vector(23 downto 1);
+               MemDB:         inout std_logic_vector(15 downto 0)
+       );
+end mem_mux;
+
+
+architecture behavioral of mem_mux is
+
+       --signal oe: std_logic;
+       signal we: std_logic;
+
+begin
+
+       MemOE   <= s0_MemOE   when sel = '0' else s1_MemOE;
+       MemWR   <= s0_MemWR   when sel = '0' else s1_MemWR;
+       RamAdv  <= s0_RamAdv  when sel = '0' else s1_RamAdv;
+       RamCS   <= s0_RamCS   when sel = '0' else s1_RamCS;
+       RamClk  <= s0_RamClk  when sel = '0' else s1_RamClk;
+       RamCRE  <= s0_RamCRE  when sel = '0' else s1_RamCRE;
+       RamUB   <= s0_RamUB   when sel = '0' else s1_RamUB;
+       RamLB   <= s0_RamLB   when sel = '0' else s1_RamLB;
+       FlashRp <= s0_FlashRp when sel = '0' else s1_FlashRp;
+       FlashCS <= s0_FlashCS when sel = '0' else s1_FlashCS;
+       MemAdr  <= s0_MemAdr  when sel = '0' else s1_MemAdr;
+
+       --oe      <= s0_MemOE   when sel = '0' else s1_MemOE;
+       we      <= s0_MemWR   when sel = '0' else s1_MemWR;
+       MemDB   <= (others => 'Z') when we = '1' else
+                  s0_MemDB_o when sel = '0' else s1_MemDB_o;
+
+       s0_RamWait    <= RamWait;
+       s0_FlashStSts <= FlashStSts;
+       s0_MemDB_i    <= MemDB;
+
+       s1_RamWait    <= RamWait;
+       s1_FlashStSts <= FlashStSts;
+       s1_MemDB_i    <= MemDB;
+
+end behavioral;
diff --git a/libraries/nexys2/mem_wb8_0.vhd b/libraries/nexys2/mem_wb8_0.vhd
new file mode 100644 (file)
index 0000000..45d9003
--- /dev/null
@@ -0,0 +1,149 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+use ieee.numeric_std.all;
+
+
+entity mem_wb8_0 is
+       port (
+               -- Wishbone SYSCON
+               rst_i:       in  std_logic;
+               clk_i:       in  std_logic;
+
+               -- Wishbone system interface
+               cyc_i:       in  std_logic;
+               stb_i:       in  std_logic;
+               we_i:        in  std_logic;
+               ack_o:       out std_logic;
+               adr_i:       in  std_logic_vector(24 downto 0);
+               dat_i:       in  std_logic_vector(7 downto 0);
+               dat_o:       out std_logic_vector(7 downto 0);
+
+               -- Configuration
+               wait_cycles: in  std_logic_vector(4 downto 0);
+
+               -- Memory interface
+               MemOE:       out std_logic;
+               MemWR:       out std_logic;
+               RamAdv:      out std_logic;
+               RamCS:       out std_logic;
+               RamClk:      out std_logic;
+               RamCRE:      out std_logic;
+               RamUB:       out std_logic;
+               RamLB:       out std_logic;
+               RamWait:     in  std_logic;
+               FlashRp:     out std_logic;
+               FlashCS:     out std_logic;
+               FlashStSts:  in  std_logic;
+               MemAdr:      out std_logic_vector(23 downto 1);
+               MemDB_i:     in  std_logic_vector(15 downto 0);  -- Inbound: from memory to device
+               MemDB_o:     out std_logic_vector(15 downto 0)   -- Outbound: from device to memory
+       );
+end mem_wb8_0;
+
+
+architecture behavioral of mem_wb8_0 is
+
+       type state_t is (S_IDLE, S_WAIT, S_DONE);
+
+       signal state_reg:      state_t;
+       signal state_next:     state_t;
+
+       signal wait_count_reg: unsigned(4 downto 0);
+       signal wait_reset:     std_logic;
+       signal wait_done:      std_logic;
+
+       signal mem_enable:     std_logic;
+
+       signal mdr_reg:        std_logic_vector(15 downto 0);
+       signal mdr_ld:         std_logic;
+
+begin
+
+       process (rst_i, clk_i, state_next)
+       begin
+               if rising_edge(clk_i) then
+                       state_reg <= state_next;
+               end if;
+       end process;
+
+       process (state_reg, cyc_i, stb_i, we_i, wait_done)
+       begin
+               state_next <= state_reg;
+               wait_reset <= '1';
+               mem_enable <= '0';
+               ack_o      <= '0';
+               MemOE      <= '1';
+               MemWR      <= '1';
+               mdr_ld     <= '0';
+
+               case state_reg is
+                       when S_IDLE =>
+                               -- Returning to this state for one clock cycle between ram
+                               -- writes gives enough time for the ram to self-refresh, at
+                               -- least at 50 MHz - more inactive cycles are needed if faster
+                               -- (Ram CE must be high 15us every 4us)
+                               if cyc_i = '1' and stb_i = '1' then
+                                       state_next <= S_WAIT;
+                               end if;
+
+                       when S_WAIT =>
+                               wait_reset <= '0';
+                               mem_enable <= '1';
+                               MemOE      <= we_i;
+                               MemWR      <= not we_i;
+                               if wait_done = '1' then
+                                       --ack_o      <= '1';
+                                       mdr_ld     <= '1';
+                                       --state_next <= S_IDLE;
+                                       state_next <= S_DONE;
+                               end if;
+
+                       when S_DONE =>
+                               ack_o      <= '1';
+                               state_next <= S_IDLE;
+
+                       when others =>
+                               state_next <= S_IDLE;
+               end case;
+       end process;
+
+       -- Little-endian memory interface
+       RamCS                <= not (mem_enable and adr_i(24));
+       RamAdv               <= '0';
+       RamClk               <= '0';
+       RamCRE               <= '0';
+       RamUB                <= not adr_i(0);
+       RamLB                <=     adr_i(0);
+       FlashCS              <= not (mem_enable and (not adr_i(24)));
+       FlashRp              <= '1';
+       MemAdr               <= adr_i(23 downto 1);
+       MemDB_o(15 downto 8) <= dat_i when adr_i(0) = '1' else (others => '0');
+       MemDB_o( 7 downto 0) <= dat_i;
+       --dat_o                <= MemDB_i(15 downto 8) when adr_i(0) = '1' else MemDB_i(7 downto 0);
+       dat_o                <= mdr_reg(15 downto 8) when adr_i(0) = '1' else mdr_reg(7 downto 0);
+
+       -- Wait cycle counter
+       process (rst_i, clk_i, wait_reset, wait_count_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' or wait_reset = '1' then
+                               wait_count_reg <= unsigned(wait_cycles);
+                       elsif wait_done = '0' then
+                               wait_count_reg <= wait_count_reg - 1;
+                       end if;
+               end if;
+       end process;
+       wait_done <= not or_reduce(std_logic_vector(wait_count_reg));
+
+       -- Memory data register
+       process (clk_i, mdr_ld)
+       begin
+               if rising_edge(clk_i) then
+                       if mdr_ld = '1' then
+                               mdr_reg <= MemDB_i;
+                       end if;
+               end if;
+       end process;
+
+end behavioral;
diff --git a/libraries/nexys2/mem_wb_basic.vhd b/libraries/nexys2/mem_wb_basic.vhd
new file mode 100644 (file)
index 0000000..cb9531b
--- /dev/null
@@ -0,0 +1,227 @@
+--------------------------------------------------------------------------------
+-- Registers:
+--
+--    +---+---+---+---+---+---+---+---+
+--    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+--    +---+---+---+---+---+---+---+---+
+-- 0: |FLS|FLS|   |   |   |RAM|RAM|AUT|  CTRL
+--    |RST|CS |   |   |   |CS |CRE|CNT|
+--    +---+---+---+---+---+---+---+---+
+-- 1: |            ADDR_L             |  A(0) IGNORED
+--    +-------------------------------+
+-- 2: |            ADDR_M             |
+--    +-------------------------------+
+-- 3: |            ADDR_H             |
+--    +-------------------------------+
+-- 4: |            DATA_L             |  TRIGGER
+--    +-------------------------------+
+-- 5: |            DATA_H             |
+--    +-------------------------------+
+-- 6: |          (RESERVED)           |
+--    +-------------------------------+
+-- 7: |          (RESERVED)           |
+--    +-------------------------------+
+--
+--------------------------------------------------------------------------------
+
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity mem_wb_basic is
+       port (
+               -- Global wishbone signals
+               clk_i:      in  std_logic;
+               rst_i:      in  std_logic;
+
+               -- Wishbone bus signals
+               cyc_i:      in  std_logic;
+               stb_i:      in  std_logic;
+               we_i:       in  std_logic;
+               ack_o:      out std_logic;
+               adr_i:      in  std_logic_vector(2 downto 0);
+               dat_i:      in  std_logic_vector(7 downto 0);
+               dat_o:      out std_logic_vector(7 downto 0);
+
+               -- Memory interface signals
+               MemOE:      out std_logic;
+               MemWR:      out std_logic;
+               RamAdv:     out std_logic;
+               RamCS:      out std_logic;
+               RamClk:     out std_logic;
+               RamCRE:     out std_logic;
+               RamUB:      out std_logic;
+               RamLB:      out std_logic;
+               RamWait:    in  std_logic;
+               FlashRp:    out std_logic;
+               FlashCS:    out std_logic;
+               FlashStSts: in  std_logic;
+               MemAdr:     out std_logic_vector(23 downto 1);
+               MemDB_i:    in  std_logic_vector(15 downto 0);  -- Inbound: from memory to device
+               MemDB_o:    out std_logic_vector(15 downto 0)   -- Outbound: from device to memory
+       );
+end mem_wb_basic;
+
+
+architecture behavioral of mem_wb_basic is
+
+       type state is (S_READY, S_WAIT_0, S_WAIT_1, S_WAIT_2, S_WAIT_3, S_WAIT_4, S_WAIT_5, S_DONE);
+
+       -- CTRL register bits:
+       -- 2 - ram_cre
+       -- 3 - ram_cs
+       -- 6 - flash_cs
+       -- 7 - flash_rp
+       signal ctrl_reg:     std_logic_vector( 7 downto 0);
+       signal addr_reg:     std_logic_vector(23 downto 0);
+       signal data_reg:     std_logic_vector(15 downto 0);
+
+       signal ctrl_fls_rst: std_logic;
+       signal ctrl_fls_cs:  std_logic;
+       signal ctrl_ram_cs:  std_logic;
+       signal ctrl_ram_cre: std_logic;
+       signal ctrl_aut_cnt: std_logic;
+
+       signal cur_state_reg: state;
+       signal next_state:    state;
+       signal data_latch:    std_logic;
+       signal addr_inc:      std_logic;
+
+begin
+
+       -- Control signals
+       ctrl_fls_rst <= ctrl_reg(7);
+       ctrl_fls_cs  <= ctrl_reg(6);
+       ctrl_ram_cs  <= ctrl_reg(2);
+       ctrl_ram_cre <= ctrl_reg(1);
+       ctrl_aut_cnt <= ctrl_reg(0);
+
+       -- Memory signals
+       RamAdv  <= '0';
+       RamCS   <= ctrl_ram_cs or not cyc_i;  -- Gate RAM CS to allow refresh
+       RamClk  <= '0';
+       RamCRE  <= ctrl_ram_cre;
+       RamUB   <= '0';
+       RamLB   <= '0';
+       FlashRp <= ctrl_fls_rst;
+       FlashCS <= ctrl_fls_cs;
+       MemAdr  <= addr_reg(23 downto 1);
+       MemDB_o <= data_reg;
+
+       -- Wishbone signals
+       with adr_i select dat_o <=
+               ctrl_reg               when "000",
+               addr_reg( 7 downto  0) when "001",
+               addr_reg(15 downto  8) when "010",
+               addr_reg(23 downto 16) when "011",
+               data_reg( 7 downto  0) when "100",
+               data_reg(15 downto  8) when "101",
+               (others => '1')        when others;
+
+       -- Wishbone bus
+       process (clk_i, rst_i,
+                cyc_i, stb_i, we_i, adr_i, dat_i,
+                        cur_state_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               ctrl_reg <= "11001000";
+                               addr_reg <= (others => '0');
+                               data_reg <= (others => '0');
+                               cur_state_reg <= S_READY;
+                       else
+                               cur_state_reg <= next_state;
+
+                               if ctrl_aut_cnt = '1' and addr_inc = '1' then
+                                       addr_reg <= std_logic_vector(unsigned(addr_reg) + 2);
+                               end if;
+
+                               if cyc_i = '1' and stb_i = '1' and we_i = '1' then
+                                       case adr_i is
+                                               when "000"  => ctrl_reg               <= dat_i;
+                                               when "001"  => addr_reg( 7 downto  0) <= dat_i;
+                                               when "010"  => addr_reg(15 downto  8) <= dat_i;
+                                               when "011"  => addr_reg(23 downto 16) <= dat_i;
+                                               when "100"  => data_reg( 7 downto  0) <= dat_i;
+                                               when "101"  => data_reg(15 downto  8) <= dat_i;
+                                               when others => null;
+                                       end case;
+                               end if;
+
+                               if data_latch = '1' then
+                                       data_reg <= MemDB_i;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       process (cur_state_reg,
+                cyc_i, stb_i, we_i, adr_i)
+       begin
+               next_state <= cur_state_reg;
+               data_latch <= '0';
+               addr_inc   <= '0';
+               ack_o      <= '0';  -- TODO: test with this removed if it stops working
+
+               MemOE <= '1';
+               MemWR <= '1';
+
+               case cur_state_reg is
+                       when S_READY =>
+                               if adr_i = "100" then
+                                       -- Reads/writes to DATA_LO trigger memory cycles,
+                                       -- which take longer than one clock cycle
+                                       ack_o <= '0';
+                                       if cyc_i = '1' and stb_i = '1' then
+                                               next_state <= S_WAIT_0;
+                                       end if;
+                               else
+                                       ack_o <= '1';
+                               end if;
+
+                       when S_WAIT_0 =>
+                               MemOE <= we_i;
+                               MemWR <= not we_i;
+                               next_state <= S_WAIT_1;
+
+                       when S_WAIT_1 =>
+                               MemOE <= we_i;
+                               MemWR <= not we_i;
+                               next_state <= S_WAIT_2;
+
+                       when S_WAIT_2 =>
+                               MemOE <= we_i;
+                               MemWR <= not we_i;
+                               next_state <= S_WAIT_3;
+
+                       when S_WAIT_3 =>
+                               MemOE <= we_i;
+                               MemWR <= not we_i;
+                               next_state <= S_WAIT_4;
+
+                       when S_WAIT_4 =>
+                               MemOE <= we_i;
+                               MemWR <= not we_i;
+                               next_state <= S_WAIT_5;
+
+                       when S_WAIT_5 =>
+                               MemOE <= we_i;
+                               MemWR <= not we_i;
+                               data_latch <= not we_i;
+                               next_state <= S_DONE;
+
+                       when S_DONE =>
+                               MemOE <= we_i;
+                               MemWR <= '1';
+                               ack_o <= '1';
+                               addr_inc <= '1';
+                               next_state <= S_READY;
+
+                       when others =>
+                               next_state <= S_READY;
+               end case;
+       end process;
+
+end behavioral;
diff --git a/libraries/nexys2/seven_seg_hex.vhd b/libraries/nexys2/seven_seg_hex.vhd
new file mode 100644 (file)
index 0000000..04b2c77
--- /dev/null
@@ -0,0 +1,36 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity seven_seg_hex is
+       port (
+               data_in: in  std_logic_vector(3 downto 0);
+               seg_out: out std_logic_vector(6 downto 0)
+       );
+end seven_seg_hex;
+
+
+architecture behavioral of seven_seg_hex is
+begin
+
+       with data_in select seg_out <=
+               "1000000" when "0000",
+               "1111001" when "0001",
+               "0100100" when "0010",
+               "0110000" when "0011",
+               "0011001" when "0100",
+               "0010010" when "0101",
+               "0000010" when "0110",
+               "1111000" when "0111",
+               "0000000" when "1000",
+               "0010000" when "1001",
+               "0001000" when "1010",
+               "0000011" when "1011",
+               "1000110" when "1100",
+               "0100001" when "1101",
+               "0000110" when "1110",
+               "0001110" when "1111",
+               "1111111" when others;
+
+end behavioral;
diff --git a/libraries/nexys2/seven_seg_mux.vhd b/libraries/nexys2/seven_seg_mux.vhd
new file mode 100644 (file)
index 0000000..cddb127
--- /dev/null
@@ -0,0 +1,57 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity seven_seg_mux is
+       port (
+               clk_in:   in  std_logic;
+
+               seg_0_in: in  std_logic_vector(6 downto 0);
+               seg_1_in: in  std_logic_vector(6 downto 0);
+               seg_2_in: in  std_logic_vector(6 downto 0);
+               seg_3_in: in  std_logic_vector(6 downto 0);
+               dps_in:   in  std_logic_vector(3 downto 0);
+
+               seg_out:  out std_logic_vector(6 downto 0);
+               dp_out:   out std_logic;
+               an_out:   out std_logic_vector(3 downto 0)
+       );
+end seven_seg_mux;
+
+
+architecture Behavioral of seven_seg_mux is
+       signal an_reg: unsigned(1 downto 0);
+begin
+
+       -- Anode selection
+
+       process(clk_in)
+       begin
+               if rising_edge(clk_in) then
+                       an_reg <= an_reg + 1;
+               end if;
+       end process;
+
+       with an_reg select an_out <=
+               "1110" when "00",
+               "1101" when "01",
+               "1011" when "10",
+               "0111" when others;
+
+
+       -- Select by anode
+
+       with an_reg select seg_out <=
+               seg_0_in when "00",
+               seg_1_in when "01",
+               seg_2_in when "10",
+               seg_3_in when others;
+
+       with an_reg select dp_out <=
+               dps_in(0) when "00",
+               dps_in(1) when "01",
+               dps_in(2) when "10",
+               dps_in(3) when others;
+
+end Behavioral;
diff --git a/libraries/nexys2/tests/program.sh b/libraries/nexys2/tests/program.sh
new file mode 100755 (executable)
index 0000000..6d2fed4
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f test_nexys2_mem_wb8_0.bit prog
+
diff --git a/libraries/nexys2/tests/test_nexys2_mem_wb8_0.vhd b/libraries/nexys2/tests/test_nexys2_mem_wb8_0.vhd
new file mode 100644 (file)
index 0000000..d86880d
--- /dev/null
@@ -0,0 +1,220 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library utility;
+
+library work;
+
+
+entity test_nexys2_mem_wb8_0 is
+       port (
+               clk_50:     in    std_logic;
+
+               DB:         inout std_logic_vector(7 downto 0);
+               EppWRITE:   in    std_logic;
+               EppASTB:    in    std_logic;
+               EppDSTB:    in    std_logic;
+               EppWAIT:    out   std_logic;
+
+               MemOE:      out   std_logic;
+               MemWR:      out   std_logic;
+               RamAdv:     out   std_logic;
+               RamCS:      out   std_logic;
+               RamClk:     out   std_logic;
+               RamCRE:     out   std_logic;
+               RamLB:      out   std_logic;
+               RamUB:      out   std_logic;
+               RamWait:    in    std_logic;
+               FlashRp:    out   std_logic;
+               FlashCS:    out   std_logic;
+               FlashStSts: in    std_logic;
+               MemAdr:     out   std_logic_vector(23 downto 1);
+               MemDB:      inout std_logic_vector(15 downto 0);
+
+               seg:        out   std_logic_vector(6 downto 0);
+               dp:         out   std_logic;
+               an:         out   std_logic_vector(3 downto 0);
+               Led:        out   std_logic_vector(7 downto 0);
+               sw:         in    std_logic_vector(7 downto 0);
+               btn:        in    std_logic_vector(3 downto 0)
+       );
+end test_nexys2_mem_wb8_0;
+
+
+architecture behavioral of test_nexys2_mem_wb8_0 is
+
+       signal clk:          std_logic;
+       signal rst:          std_logic;
+       signal cyc:          std_logic;
+       signal mem_cyc:      std_logic;
+       signal host_cyc:     std_logic;
+       signal stb:          std_logic;
+       signal we:           std_logic;
+       signal ack:          std_logic;
+       signal mem_ack:      std_logic;
+       signal host_ack:     std_logic;
+       signal adr:          std_logic_vector(7 downto 0);
+       signal mem_adr:      std_logic_vector(24 downto 0);
+       signal dat_mosi:     std_logic_vector(7 downto 0);
+       signal dat_miso:     std_logic_vector(7 downto 0);
+       signal mem_miso:     std_logic_vector(7 downto 0);
+       signal host_miso:    std_logic_vector(7 downto 0);
+
+       signal d_MemOE:      std_logic;
+       signal d_MemWR:      std_logic;
+       signal d_RamAdv:     std_logic;
+       signal d_RamCS:      std_logic;
+       signal d_RamClk:     std_logic;
+       signal d_RamCRE:     std_logic;
+       signal d_RamUB:      std_logic;
+       signal d_RamLB:      std_logic;
+       signal d_RamWait:    std_logic;
+       signal d_FlashRp:    std_logic;
+       signal d_FlashCS:    std_logic;
+       signal d_FlashStSts: std_logic;
+       signal d_MemAdr:     std_logic_vector(23 downto 1);
+       signal d_MemDB_i:    std_logic_vector(15 downto 0);
+       signal d_MemDB_o:    std_logic_vector(15 downto 0);
+
+       signal temp:         std_logic_vector(7 downto 0);
+
+begin
+
+       mem_adr <= adr(6) & "000000000000000000" & adr(5 downto 0);
+       e_mem: entity work.mem_wb8_0
+               port map (
+                       rst_i       => rst,
+                       clk_i       => clk,
+
+                       cyc_i       => mem_cyc,
+                       stb_i       => stb,
+                       we_i        => we,
+                       ack_o       => mem_ack,
+                       adr_i       => mem_adr,
+                       dat_i       => dat_mosi,
+                       dat_o       => mem_miso,
+
+                       wait_cycles => temp(4 downto 0),
+
+                       MemOE       => d_MemOE,
+                       MemWR       => d_MemWR,
+                       RamAdv      => d_RamAdv,
+                       RamCS       => d_RamCS,
+                       RamClk      => d_RamClk,
+                       RamCRE      => d_RamCRE,
+                       RamUB       => d_RamUB,
+                       RamLB       => d_RamLB,
+                       RamWait     => d_RamWait,
+                       FlashRp     => d_FlashRp,
+                       FlashCS     => d_FlashCS,
+                       FlashStSts  => d_FlashStSts,
+                       MemAdr      => d_MemAdr,
+                       MemDB_i     => d_MemDB_i,
+                       MemDB_o     => d_MemDB_o
+               );
+
+       e_wb_mux: entity utility.wb_mux2
+               generic map (
+                       WIDTH => 8
+               )
+               port map (
+                       sel     => adr(7),
+
+                       cyc_i   => cyc,
+                       ack_o   => ack,
+                       dat_o   => dat_miso,
+
+                       cyc_o_0 => mem_cyc,
+                       ack_i_0 => mem_ack,
+                       dat_i_0 => mem_miso,
+
+                       cyc_o_1 => host_cyc,
+                       ack_i_1 => host_ack,
+                       dat_i_1 => host_miso
+               );
+
+       e_wb_dbg: entity work.wishbone_debugger
+               port map (
+                       clk_o  => clk,
+                       rst_o  => rst,
+
+                       cyc_o  => cyc,
+                       stb_o  => stb,
+                       we_o   => we,
+                       ack_i  => ack,
+                       adr_o  => adr,
+                       dat_o  => dat_mosi,
+                       dat_i  => dat_miso,
+
+                       clk_50 => clk_50,
+                       seg    => seg,
+                       dp     => dp,
+                       an     => an,
+                       sw     => sw,
+                       btn    => btn
+               );
+
+       e_host_ctrl: entity work.host_ctrl
+               port map (
+                       clk_i        => clk,
+                       rst_i        => rst,
+
+                       d_rst_o      => open,
+                       d_flags_o    => open,
+                       debug_i      => (others => '0'),
+                       debug_o      => open,
+
+                       d_cyc_i      => host_cyc,
+                       d_stb_i      => stb,
+                       d_we_i       => we,
+                       d_ack_o      => host_ack,
+                       d_adr_i      => adr(2 downto 0),
+                       d_dat_i      => dat_mosi,
+                       d_dat_o      => host_miso,
+
+                       d_MemOE      => d_MemOE,
+                       d_MemWR      => d_MemWR,
+                       d_RamAdv     => d_RamAdv,
+                       d_RamCS      => d_RamCS,
+                       d_RamClk     => d_RamClk,
+                       d_RamCRE     => d_RamCRE,
+                       d_RamLB      => d_RamLB,
+                       d_RamUB      => d_RamUB,
+                       d_RamWait    => d_RamWait,
+                       d_FlashRp    => d_FlashRp,
+                       d_FlashCS    => d_FlashCS,
+                       d_FlashStSts => d_FlashStSts,
+                       d_MemAdr     => d_MemAdr,
+                       d_MemDB_o    => d_MemDB_o,
+                       d_MemDB_i    => d_MemDB_i,
+
+                       EppAstb      => EppASTB,
+                       EppDstb      => EppDSTB,
+                       EppWr        => EppWRITE,
+                       EppDB        => DB,
+                       EppWait      => EppWAIT,
+
+                       MemOE        => MemOE,
+                       MemWR        => MemWR,
+                       RamAdv       => RamAdv,
+                       RamCS        => RamCS,
+                       RamClk       => RamClk,
+                       RamCRE       => RamCRE,
+                       RamLB        => RamLB,
+                       RamUB        => RamUB,
+                       RamWait      => RamWait,
+                       FlashRp      => FlashRp,
+                       FlashCs      => FlashCs,
+                       FlashStSts   => FlashStSts,
+                       MemAdr       => MemAdr,
+                       MemDB        => MemDB,
+
+                       seg          => open,
+                       dp           => open,
+                       an           => open,
+                       Led          => temp,
+                       sw           => sw
+               );
+               Led <= temp;
+
+end behavioral;
diff --git a/libraries/nexys2/tests/test_sim_mem_wb8_0.vhd b/libraries/nexys2/tests/test_sim_mem_wb8_0.vhd
new file mode 100644 (file)
index 0000000..64e2956
--- /dev/null
@@ -0,0 +1,158 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library simulated;
+
+library work;
+
+
+entity test_sim_mem_wb8_0 is
+end test_sim_mem_wb8_0;
+
+
+architecture behavior of test_sim_mem_wb8_0 is
+
+       constant clk_i_period: time := 20 ns;
+
+       signal rst_i:       std_logic;
+       signal clk_i:       std_logic;
+
+       signal cyc_i:       std_logic;
+       signal stb_i:       std_logic;
+       signal we_i:        std_logic;
+       signal ack_o:       std_logic;
+       signal adr_i:       std_logic_vector(24 downto 0);
+       signal dat_i:       std_logic_vector(7 downto 0);
+       signal dat_o:       std_logic_vector(7 downto 0);
+
+       signal wait_cycles: std_logic_vector(4 downto 0);
+
+       signal MemOE:       std_logic;
+       signal MemWR:       std_logic;
+       signal RamAdv:      std_logic;
+       signal RamCS:       std_logic;
+       signal RamClk:      std_logic;
+       signal RamCRE:      std_logic;
+       signal RamUB:       std_logic;
+       signal RamLB:       std_logic;
+       signal RamWait:     std_logic;
+       signal FlashRp:     std_logic;
+       signal FlashCS:     std_logic;
+       signal FlashStSts:  std_logic;
+       signal MemAdr:      std_logic_vector(23 downto 1);
+       signal MemDB_i:     std_logic_vector(15 downto 0);
+       signal MemDB_o:     std_logic_vector(15 downto 0);
+       signal MemDB:       std_logic_vector(15 downto 0);
+
+       type test_name_t is (
+               NONE,
+               RD_FLASH
+       );
+
+       signal test:        test_name_t;
+
+begin
+
+       p_test: process
+       begin
+               -- Initial values
+               test        <= NONE;
+               cyc_i       <= '0';
+               stb_i       <= '0';
+               we_i        <= '0';
+               adr_i       <= (others => '0');
+               dat_i       <= (others => '0');
+               wait_cycles <= "00110";
+
+               -- Reset
+               rst_i <= '1';
+               wait for clk_i_period * 2;
+               rst_i <= '0';
+               wait for 300 ns;  -- T_PHEL for flash
+
+               -- Read from flash
+               test  <= RD_FLASH;
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "0000000000000000000000000";
+               wait until ack_o = '1';
+               cyc_i <= '0';
+               stb_i <= '0';
+
+               wait;
+       end process;
+
+       uut: entity work.mem_wb8_0
+               port map (
+                       rst_i       => rst_i,
+                       clk_i       => clk_i,
+
+                       cyc_i       => cyc_i,
+                       stb_i       => stb_i,
+                       we_i        => we_i,
+                       ack_o       => ack_o,
+                       adr_i       => adr_i,
+                       dat_i       => dat_i,
+                       dat_o       => dat_o,
+
+                       wait_cycles => wait_cycles,
+
+                       MemOE       => MemOE,
+                       MemWR       => MemWR,
+                       RamAdv      => RamAdv,
+                       RamCS       => RamCS,
+                       RamClk      => RamClk,
+                       RamCRE      => RamCRE,
+                       RamUB       => RamUB,
+                       RamLB       => RamLB,
+                       RamWait     => RamWait,
+                       FlashRp     => FlashRp,
+                       FlashCS     => FlashCS,
+                       FlashStSts  => FlashStSts,
+                       MemAdr      => MemAdr,
+                       MemDB_i     => MemDB_i,
+                       MemDB_o     => MemDB_o
+               );
+
+       MemDB <= (others => 'Z') when MemWR = '1' else MemDB_o;
+
+       e_ram: entity simulated.mt45w8mw16bgx
+               port map (
+                       a     => MemAdr,
+                       dq    => MemDB,
+                       clk   => RamClk,
+                       adv_n => RamAdv,
+                       cre   => RamCRE,
+                       ce_n  => RamCS,
+                       oe_n  => MemOE,
+                       we_n  => MemWR,
+                       lb_n  => RamLB,
+                       ub_n  => RamUB,
+                       rwait => RamWait
+               );
+
+       e_flash: entity simulated.js28f128j3d75
+               port map (
+                       a(23 downto 1) => MemAdr,
+                       a(0)           => '0',
+                       d              => MemDB,
+                       ce(2 downto 1) => "00",
+                       ce(0)          => FlashCS,
+                       rp_n           => FlashRp,
+                       oe_n           => MemOE,
+                       we_n           => MemWR,
+                       sts            => FlashStSts,
+                       byte_n         => '1',
+                       vpen           => '1'
+               );
+
+       clk_i_process: process
+       begin
+               clk_i <= '0';
+               wait for clk_i_period/2;
+               clk_i <= '1';
+               wait for clk_i_period/2;
+       end process;
+
+end;
diff --git a/libraries/nexys2/wishbone_debugger.vhd b/libraries/nexys2/wishbone_debugger.vhd
new file mode 100644 (file)
index 0000000..baa5751
--- /dev/null
@@ -0,0 +1,253 @@
+--------------------------------------------------------------------------------
+-- Nexys2 Manual Wishbone Bus Master
+--
+-- The purpose is to provide a ready-made harness to manually test and debug
+-- Wishbone bus devices by hand on a Nexys2 board
+--
+-- The intent is to use the Wishbone interface to test the functionality of a
+-- device, not to test the Wishbone interface itself (The device's interface is
+-- assumed to be functional)
+--
+--
+--       address    data
+--      /-------\ /-------\
+--       --   --   --   --
+--      |  | |  | |  | |  |  Seven-segment display
+--       --   --   --   --
+--      |  | |  | |  | |  |
+--       --   -- o --   --
+--
+--       /\   /\   /\   /\   Pushbuttons
+--       \/   \/   \/   \/
+--      addr read write reset
+--
+--
+-- Switches are used to enter an 8-bit value
+--
+-- Address button latches value entered on the switchs into an internal address
+-- holding register, which is displayed in hex on the left two 7seg digits
+--
+-- Read button performs a Wishbone bus read cycle, latching the value into an
+-- internal data holding register, which is displayed in hex on the right two
+-- 7seg digits
+--
+-- Write button performs a Wishbone bus write cycle, writing the value entered
+-- on the switches and also displaying the written value in hex on the right two
+-- 7seg digits
+--
+-- The LEDs on the Nexys2 board are left available for flags put out by the DUT
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library utility;
+
+
+entity wishbone_debugger is
+       port (
+               -- Wishbone SYSCON
+               clk_o:  out std_logic;
+               rst_o:  out std_logic;
+
+               -- wishbone bus master, 8-bit address, 8-bit data
+               cyc_o:  out std_logic;
+               stb_o:  out std_logic;
+               we_o:   out std_logic;
+               ack_i:  in  std_logic;
+               adr_o:  out std_logic_vector(7 downto 0);
+               dat_o:  out std_logic_vector(7 downto 0);
+               dat_i:  in  std_logic_vector(7 downto 0);
+
+               -- Nexys2 IO pins
+               clk_50: in  std_logic;
+               seg:    out std_logic_vector(6 downto 0);
+               dp:     out std_logic;
+               an:     out std_logic_vector(3 downto 0);
+               sw:     in  std_logic_vector(7 downto 0);
+               btn:    in  std_logic_vector(3 downto 0)
+       );
+end wishbone_debugger;
+
+
+architecture behavioral of wishbone_debugger is
+
+       type state_t is (S_IDLE, S_READ, S_WRITE);
+
+       signal rst:         std_logic;
+       signal clk:         std_logic;
+
+       signal rd_stb:      std_logic;
+       signal wr_stb:      std_logic;
+       signal adr_stb:     std_logic;
+       signal adr_reg:     std_logic_vector(7 downto 0);
+       signal dat_r_stb:   std_logic;
+       signal dat_w_stb:   std_logic;
+       signal dat_reg:     std_logic_vector(7 downto 0);
+
+       signal state_reg:   state_t;
+       signal state_next:  state_t;
+
+       -- Signal conditioning
+       signal clk_div_reg: unsigned(15 downto 0);
+       signal seg_3:       std_logic_vector(6 downto 0);
+       signal seg_2:       std_logic_vector(6 downto 0);
+       signal seg_1:       std_logic_vector(6 downto 0);
+       signal seg_0:       std_logic_vector(6 downto 0);
+
+begin
+
+       -- Wishbone controller
+
+       process (rst, clk, state_next)
+       begin
+               if rising_edge(clk) then
+                       if rst = '1' then
+                               state_reg <= S_IDLE;
+                       else
+                               state_reg <= state_next;
+                       end if;
+               end if;
+       end process;
+
+       process (state_reg, rd_stb, wr_stb, ack_i)
+       begin
+               state_next <= state_reg;
+               cyc_o      <= '0';
+               stb_o      <= '0';
+               we_o       <= '0';
+               dat_r_stb  <= '0';
+               dat_w_stb  <= '0';
+
+               case state_reg is
+                       when S_IDLE =>
+                               if rd_stb = '1' then
+                                       state_next <= S_READ;
+                               elsif wr_stb = '1' then
+                                       state_next <= S_WRITE;
+                               end if;
+
+                       when S_READ =>
+                               cyc_o <= '1';
+                               stb_o <= '1';
+                               if ack_i = '1' then
+                                       dat_r_stb <= '1';
+                                       state_next <= S_IDLE;
+                               end if;
+
+                       when S_WRITE =>
+                               cyc_o <= '1';
+                               stb_o <= '1';
+                               we_o  <= '1';
+                               if ack_i = '1' then
+                                       dat_w_stb <= '1';
+                                       state_next <= S_IDLE;
+                               end if;
+
+                       when others =>
+                               state_next <= S_IDLE;
+               end case;
+       end process;
+
+       process (rst, clk, adr_stb, sw)
+       begin
+               if rising_edge(clk) then
+                       if rst = '1' then
+                               adr_reg <= (others => '0');
+                       elsif adr_stb = '1' then
+                               adr_reg <= sw;
+                       end if;
+               end if;
+       end process;
+
+       process (rst, clk, dat_r_stb, dat_w_stb, dat_i, sw)
+       begin
+               if rising_edge(clk) then
+                       if rst = '1' then
+                               dat_reg <= (others => '0');
+                       elsif dat_r_stb = '1' then
+                               dat_reg <= dat_i;
+                       elsif dat_w_stb = '1' then
+                               dat_reg <= sw;
+                       end if;
+               end if;
+       end process;
+
+       rst_o <= rst;
+       clk_o <= clk;
+       adr_o <= adr_reg;
+       dat_o <= sw;
+
+
+       -- Signal conditioning
+
+       process (clk_50)
+       begin
+               if rising_edge(clk_50) then
+                       clk_div_reg <= clk_div_reg + 1;
+               end if;
+       end process;
+
+       clk     <= clk_50;
+       rst     <= btn(0);  -- No debouncing necessary
+       adr_stb <= btn(3);  -- No debouncing necessary
+
+       e_debounce_rd: entity utility.debounce
+               port map (
+                       clk      => clk,
+                       clk_samp => clk_div_reg(15),
+                       btn_deb  => open,
+                       btn_rise => rd_stb,
+                       btn_fall => open,
+                       btn      => btn(2)
+               );
+
+       e_debounce_wr: entity utility.debounce
+               port map (
+                       clk      => clk,
+                       clk_samp => clk_div_reg(15),
+                       btn_deb  => open,
+                       btn_rise => wr_stb,
+                       btn_fall => open,
+                       btn      => btn(1)
+               );
+
+       e_hex3: entity work.seven_seg_hex
+               port map (
+                       data_in => adr_reg(7 downto 4),
+                       seg_out => seg_3
+               );
+
+       e_hex2: entity work.seven_seg_hex
+               port map (
+                       data_in => adr_reg(3 downto 0),
+                       seg_out => seg_2
+               );
+
+       e_hex1: entity work.seven_seg_hex
+               port map (
+                       data_in => dat_reg(7 downto 4),
+                       seg_out => seg_1
+               );
+
+       e_hex0: entity work.seven_seg_hex
+               port map (
+                       data_in => dat_reg(3 downto 0),
+                       seg_out => seg_0
+               );
+
+       e_7seg_mux: entity work.seven_seg_mux
+               port map (
+                       clk_in   => clk_div_reg(15),
+                       seg_0_in => seg_0,
+                       seg_1_in => seg_1,
+                       seg_2_in => seg_2,
+                       seg_3_in => seg_3,
+                       dps_in   => "1011",
+                       seg_out  => seg,
+                       dp_out   => dp,
+                       an_out   => an
+               );
+
+end behavioral;
diff --git a/libraries/ps2/ps2_host.vhd b/libraries/ps2/ps2_host.vhd
new file mode 100644 (file)
index 0000000..00ea8b1
--- /dev/null
@@ -0,0 +1,482 @@
+--------------------------------------------------------------------------------
+-- Notes:
+--
+-- The interface to the system is Wishbone-like - ACK and DAT signals are only
+-- considered valid when the corresponding STB signal is asserted, and both may
+-- be asserted or contain data even when STB is not asserted, which must be
+-- ignored.  This applies to both tx_* and rx_* interfaces.
+--
+-- If any transfer is in progress when the system signals that it wants to
+-- transmit a byte via tx_stb_i, the controller will stall the system by not
+-- asserting tx_ack_o until both the current transfer is finished and, if the
+-- transfer is from device to the host, the system acknowledges the received
+-- byte by asserting rx_ack_i.
+--
+-- The controller will not accept new bytes to transmit to the device when
+-- inhibit is asserted, and will stall the system by not asserting tx_ack_o.
+-- If a host to device transfer is already in progress when inhibit is
+-- asserted, the transfer is completed before inhibiting the device.
+--
+-- If a device to host transfer is in progress when inhibit is asserted, the
+-- transfer will be cancelled and received bits discarded unless the transfer
+-- has reached the 11th (stop) bit.  If the transfer of the stop bit is in
+-- progress, the controller will finish receiving the byte and then inhibit the
+-- device.  The received byte will be signalled with rx_stb_o and must be
+-- acknowledged by the system with rx_ack_i.  This behavior is required by the
+-- PS2 serial protocol spec.
+--
+-- If auto_inhibit is asserted, the controller will inhibit the device from
+-- transmitting after each byte received from the device, and will uninhibit the
+-- device once the received byte is acknowledged by the system via rx_ack_i.
+-- Use of auto_inhibit is recommended unless it is known that a device will not
+-- behave predictably when inhibited.
+--
+-- Some of the error signals have imprecise timings, which is to say that it
+-- cannot strictly be determined when the error signal should be checked or
+-- possibly which transfer caused the error.  If transfer errors occur, you have
+-- bigger problems that won't be solved by knowing which transfer caused the
+-- error, so this is fine.  The variety in types of distinguishable errors is
+-- certainly overkill, as the response to any errors is generally:
+-- 1. Discard data for receive errors, retry for transmit errors
+-- 2. Reset the controller interface if (1) doesn't work
+-- 3. Give up completely if neither (1) nor (2) work
+--
+-- Error signals:
+-- tx_err_nack    - Asserted when the device fails to acknowledge a byte
+--                  transmitted from the host
+--                  Always valid, but will indicate a detected error only upon
+--                  transfer completion of each host-transmitted byte - without
+--                  "done" signalling, the timing relative to the beginning of a
+--                  transfer is imprecise
+--                  Deasserted when the interface is reset
+-- tx_err_timeout - Asserted when the device fails to transition the serial CLK
+--                  line within some time limit during host to device transfer
+--                  Always valid, but timing of error detection (and the error
+--                  itself) relative to the beginning of a transfer is imprecise
+--                  Deasserted when the interface is reset
+-- rx_err_timeout - Asserted when the device fails to transition the serial CLK
+--                  line within some time limit during device to host transfer
+--                  Always valid, but timing of error detection (and the error
+--                  itself) relative to the beginning of a transfer is imprecise,
+--                  and rx_stb_o will not be asserted after a receive timeout as
+--                  no complete transfer took place
+--                  Deasserted when the interface is reset
+-- rx_err_missed  - Asserted when the device begins another transfer to the host
+--                  before the system has acknowledged a previous transfer
+--                  Always valid, but timing of error detection (and the error
+--                  itself) is imprecise
+--                  Deasserted when the previously received byte is acknowledged
+--                  by the host via rx_ack_i
+-- rx_err_parity  - Asserted when a parity error is detected for a received byte
+--                  Valid only when rx_stb_o is asserted (may spuriously assert
+--                  while rx_stb_o is deasserted)
+--                  Recomputed with each received byte
+-- rx_err_start   - Asserted when a device to host transfer sends a one-valued
+--                  start bit
+--                  Valid only when rx_stb_o is asserted (may spuriously assert
+--                  while rx_stb_o is deasserted)
+--                  Recomputed with each received byte
+-- rx_err_stop    - Asserted when a device to host transfer sends a zero-valued
+--                  stop bit
+--                  Valid only when rx_stb_o is asserted (may spuriously assert
+--                  while rx_stb_o is deasserted)
+--                  Recomputed with each received byte
+--
+-- TODO: Accept watchdog timeout tick count as input signal from system
+-- TODO: Use separate (not watchdog timeout) tick count for transmit inhibit
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_misc.all;
+
+
+entity ps2_host is
+       port (
+               -- Wishbone SYSCON
+               rst_i:          in    std_logic;
+               clk_i:          in    std_logic;
+
+               -- Transmit
+               tx_stb_i:       in    std_logic;  -- Trigger transmission of a byte
+               tx_ack_o:       out   std_logic;  -- Ready to transmit
+               tx_dat_i:       in    std_logic_vector(7 downto 0);
+
+               -- Receive
+               rx_stb_o:       out   std_logic;  -- Byte from device is ready
+               rx_ack_i:       in    std_logic;  -- Acknowledge a received byte
+               rx_dat_o:       out   std_logic_vector(7 downto 0);
+
+               -- Configuration
+               inhibit:        in    std_logic;  -- Cancel and prevent transmission from device
+               auto_inhibit:   in    std_logic;  -- Prevent transmission from device until byte is read
+
+               -- Error signals
+               tx_err_nack:    out   std_logic;  -- Transmission not acknowledged
+               tx_err_timeout: out   std_logic;  -- Device took too long to transition clock during tx
+               rx_err_timeout: out   std_logic;  -- Device took too long to transition clock during rx
+               rx_err_missed:  out   std_logic;  -- Byte from device was missed
+               rx_err_parity:  out   std_logic;  -- Parity error detected
+               rx_err_start:   out   std_logic;  -- Start bit was not 1
+               rx_err_stop:    out   std_logic;  -- Stop bit was not 1
+
+               -- Serial bus
+               ps2_clk:        inout std_logic;
+               ps2_data:       inout std_logic
+       );
+end ps2_host;
+
+
+architecture behavioral of ps2_host is
+
+       type state_t is (
+               S_READY,    S_INHIBIT,
+               S_RX_SHIFT, S_RX_HOLD,
+               S_TX_START, S_TX_SHIFT
+       );
+
+       signal state_reg:         state_t;
+       signal next_state:        state_t;
+
+       -- Shifter and control
+       -- start bit, 8 data bits, parity bit, stop bit all included in shift register
+       signal count_reset:       std_logic;
+       signal data_count_reg:    unsigned(3 downto 0);
+       signal count_done:        std_logic;
+       signal tx_latch_en:       std_logic;
+       signal transmit:          std_logic;
+       signal shift_en:          std_logic;
+       signal data_in_parity:    std_logic;
+       signal data_shift_reg:    std_logic_vector(10 downto 0);
+
+       -- Watchdog
+       constant TICKS_200us:     unsigned(15 downto 0) := x"2710";  -- Assumes 50MHz clock
+       signal watchdog_kick:     std_logic;
+       signal watchdog_reg:      unsigned(15 downto 0);
+       signal watchdog_expire:   std_logic;
+
+       -- Error tracking registers
+       signal tx_nack_update:    std_logic;
+       signal tx_nack_reg:       std_logic;
+
+       signal tx_timeout_set:    std_logic;
+       signal tx_timeout_reg:    std_logic;
+
+       signal rx_parity_reg:     std_logic;
+
+       signal rx_timeout_set:    std_logic;
+       signal rx_timeout_reg:    std_logic;
+
+       signal rx_missed_set:     std_logic;
+       signal rx_missed_rst:     std_logic;
+       signal rx_missed_reg:     std_logic;
+
+       -- Serial bus signal conditioning
+       signal host_clk:          std_logic;
+       signal host_data:         std_logic;
+       signal ps2_clk_int:       std_logic;
+       signal ps2_data_int:      std_logic;
+       signal edge_detector_reg: std_logic_vector(1 downto 0);
+       signal ps2_rising_edge:   std_logic;
+       signal ps2_falling_edge:  std_logic;
+
+begin
+
+       ----------------------------------------------------------------------------
+       -- State machine
+
+       process (rst_i, clk_i, next_state)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               state_reg <= S_READY;
+                       else
+                               state_reg <= next_state;
+                       end if;
+               end if;
+       end process;
+
+       process (state_reg, ps2_rising_edge, ps2_falling_edge, watchdog_expire, tx_stb_i, rx_ack_i, inhibit, auto_inhibit, count_done, data_shift_reg)
+       begin
+               -- Default signals
+               next_state     <= state_reg;  -- Remain in current state
+               rx_stb_o       <= '0';        -- Don't present a received byte
+               tx_ack_o       <= '0';        -- Don't accept a transmit byte
+               tx_latch_en    <= '0';        -- Don't latch data from system to transmit
+               shift_en       <= '0';        -- Don't shift data through the serial bus or count bits
+               watchdog_kick  <= '0';        -- Allow watchdog to run
+               -- TODO: count_reset should default to 1 and be explicitly disabled when receiving
+               count_reset    <= '0';        -- Leave the bit counter alone
+               host_clk       <= '1';        -- Allow device to control serial clock line
+               host_data      <= '1';        -- Allow device to control serial data line
+               tx_timeout_set <= '0';        -- Don't flag TX timeout
+               rx_timeout_set <= '0';        -- Don't flag RX timeout
+               rx_missed_rst  <= '0';        -- Don't reset the RX missed flag
+               transmit       <= '0';
+
+               case state_reg is
+                       when S_READY =>
+                               -- Accept either new Tx or Rx byte
+                               tx_ack_o      <= '1';  -- Accept byte to transmit
+                               watchdog_kick <= '1';  -- Stop watchdog from running and reset
+                               count_reset   <= '1';  -- Reset bit counter
+                               if inhibit = '1' then
+                                       -- If inhibit requested, don't accept byte to transmit
+                                       tx_ack_o   <= '0';
+                                       next_state <= S_INHIBIT;
+                               elsif tx_stb_i = '1' then
+                                       -- Grab a byte from the system and start transmission
+                                       tx_latch_en <= '1';
+                                       next_state  <= S_TX_START;
+                               elsif ps2_falling_edge = '1' then
+                                       -- Begin receiving from device
+                                       count_reset     <= '0';  -- Allow the first falling edge to be counted
+                                       shift_en        <= '1';
+                                       next_state      <= S_RX_SHIFT;
+                               end if;
+
+                       when S_INHIBIT =>
+                               -- Don't accept any new Tx or Rx bytes until inhibit is lowered
+                               host_clk <= '0';
+                               if inhibit = '0' then
+                                       next_state <= S_READY;
+                               end if;
+
+                       when S_RX_SHIFT =>
+                               if inhibit = '1' and count_done = '0' then
+                                       -- Don't allow inhibit during last (stop) bit, otherwise
+                                       -- cancel and discard reception
+                                       next_state <= S_INHIBIT;
+                               elsif ps2_falling_edge = '1' then
+                                       -- Sample data on falling edge and kick the watchdog
+                                       watchdog_kick    <= '1';
+                                       shift_en         <= '1';
+                               --elsif ps2_rising_edge = '1' and count_done = '1' then
+                               elsif count_done = '1' then
+                                       -- Done on XrisingXedgeX of last bit
+                                       -- Don't wait for the rising edge so we can keep the clock
+                                       -- low when auto-inhibiting - this prevents us from dropping
+                                       -- the clock due to auto-inhibit and picking up our own
+                                       -- falling edge as the beginning of a new byte, causing
+                                       -- spurrious rx timeout errors
+                                       next_state <= S_RX_HOLD;
+                               elsif watchdog_expire = '1' then
+                                       -- If device stops responding, abort and return to ready
+                                       rx_timeout_set <= '1';
+                                       next_state <= S_READY;
+                               end if;
+
+                       when S_RX_HOLD =>
+                               -- Report reception to system and don't accept bytes to transmit
+                               -- until the shift register is made available by acknowledgement
+                               rx_stb_o <= '1';
+                               count_reset <= '1';  -- This prevents underflow when auto-inhibiting for a single clock cycle
+                               if auto_inhibit = '1' or inhibit = '1' then
+                                       -- If inhibit was asserted during the stop bit, the byte is
+                                       -- received, and we inhibit now - or if auto-inhibition is
+                                       -- requested after reception to avoid missing data
+                                       host_clk <= '0';
+                               end if;
+                               if rx_ack_i = '1' then
+                                       -- Upon acknowledgement, either return to ready or handle
+                                       -- inhibition that was deferred due to assertion during the
+                                       -- stop bit
+                                       rx_missed_rst <= '1';
+                                       if inhibit = '1' then
+                                               next_state <= S_INHIBIT;
+                                       else
+                                               -- We may reenter the ready state while the device is
+                                               -- still holding the clock low, since we don't wait for
+                                               -- the rising edge to enter rx_hold (to support auto-
+                                               -- inhibit)
+                                               -- This will not cause problems, as transmitting a new
+                                               -- byte begins with inhibiting the deivce for a time
+                                               -- anyway, and the device knows what it's doing when it
+                                               -- wants to send the next byte
+                                               next_state <= S_READY;
+                                       end if;
+                               end if;
+
+                       when S_TX_START =>
+                               -- Inhibit device and transmit the start bit
+                               transmit <= '1';
+                               host_clk      <= '0';
+                               host_data     <= data_shift_reg(0);
+                               if watchdog_expire = '1' then
+                                       -- Use the watchdog as a timer to inhibit the device for some time
+                                       watchdog_kick <= '1';
+                                       next_state    <= S_TX_SHIFT;
+                               end if;
+
+                       when S_TX_SHIFT =>
+                               transmit <= '1';
+                               host_data <= data_shift_reg(0);  -- Transmit data bit (will still be start bit until first falling edge)
+                               if ps2_falling_edge = '1' then
+                                       -- Change data on falling edge and kick the watchdog
+                                       -- The shift register also shifts data back in, which will catch the device ACK
+                                       watchdog_kick <= '1';
+                                       shift_en      <= '1';
+                                       if count_done = '1' then
+                                               tx_nack_update <= '1';
+                                       end if;
+                               elsif ps2_rising_edge = '1' and count_done = '1' then
+                                       -- Done on rising edge of last bit
+                                       next_state <= S_READY;
+                               elsif watchdog_expire = '1' then
+                                       -- If device stops responding, abort and return to ready
+                                       tx_timeout_set <= '1';
+                                       next_state <= S_READY;
+                               end if;
+
+                       when others =>
+                               next_state <= S_READY;
+               end case;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Error tracking registers
+
+       process (rst_i, clk_i, tx_nack_update, ps2_data_int)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               tx_nack_reg <= '0';
+                       elsif tx_nack_update = '1' then
+                               tx_nack_reg <= ps2_data_int;
+                       end if;
+               end if;
+       end process;
+       tx_err_nack <= tx_nack_reg;
+
+       process (rst_i, clk_i, tx_timeout_set)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               tx_timeout_reg <= '0';
+                       elsif tx_timeout_set = '1' then
+                               tx_timeout_reg <= '1';
+                       end if;
+               end if;
+       end process;
+       tx_err_timeout <= tx_timeout_reg;
+
+       process (rst_i, clk_i, count_reset, shift_en, rx_parity_reg, ps2_data_int)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' or count_reset = '1' then
+                               rx_parity_reg <= '0';
+                       elsif shift_en = '1' then
+                               rx_parity_reg <= rx_parity_reg xor ps2_data_int;
+                       end if;
+               end if;
+       end process;
+       rx_err_parity <= rx_parity_reg;
+       rx_err_start  <= data_shift_reg(0);
+       rx_dat_o      <= data_shift_reg(8 downto 1);
+       rx_err_stop   <= not data_shift_reg(10);
+
+       process (rst_i, clk_i, rx_timeout_set)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               rx_timeout_reg <= '0';
+                       elsif rx_timeout_set = '1' then
+                               rx_timeout_reg <= '1';
+                       end if;
+               end if;
+       end process;
+       rx_err_timeout <= rx_timeout_reg;
+
+       process (rst_i, clk_i, rx_missed_set)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' or rx_missed_rst = '1' then
+                               rx_missed_reg <= '0';
+                       elsif rx_missed_set = '1' then
+                               rx_missed_reg <= '1';
+                       end if;
+               end if;
+       end process;
+       rx_err_missed <= rx_missed_reg;
+
+
+       ----------------------------------------------------------------------------
+       -- Data shift register and bit counter
+
+       -- Count down to zero and halt
+       process (rst_i, clk_i, count_reset, shift_en, data_count_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' or count_reset = '1' then
+                               data_count_reg <= x"b";
+                       elsif shift_en = '1' then
+                               data_count_reg <= data_count_reg - 1;
+                       end if;
+               end if;
+       end process;
+       count_done <= not or_reduce(std_logic_vector(data_count_reg));
+
+       -- Transmit data latched when tx_latch_en is active
+       -- LSBs shifted out and PS2 data shifted into the MSB when shift_en is active
+       -- Used for both Tx and Rx
+       process (clk_i, tx_latch_en, shift_en, data_in_parity, tx_dat_i, ps2_data_int, data_shift_reg, transmit)
+       begin
+               if rising_edge(clk_i) then
+                       if tx_latch_en = '1' then
+                               data_shift_reg <= '1' & data_in_parity & tx_dat_i & '0';
+                       elsif shift_en = '1' then
+                               -- The "or transmit" signal forces ones to be shifted in while
+                               -- transmitting, which prevents from sampling our own start bit
+                               -- and shifting it out during the acknowledge period, which
+                               -- would acknowledge our own transmission
+                               data_shift_reg <= (ps2_data_int or transmit) & data_shift_reg(data_shift_reg'high downto 1);
+                       end if;
+               end if;
+       end process;
+       data_in_parity <= not xor_reduce(tx_dat_i);
+
+
+       ----------------------------------------------------------------------------
+       -- Watchdog timer, reset to 200us when kicked, stop at zero
+
+       process (rst_i, clk_i, watchdog_kick, watchdog_expire, watchdog_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' or watchdog_kick = '1' then
+                               watchdog_reg <= TICKS_200us;
+                       elsif watchdog_expire = '0' then
+                               watchdog_reg <= watchdog_reg - 1;
+                       end if;
+               end if;
+       end process;
+       watchdog_expire <= not or_reduce(std_logic_vector(watchdog_reg));
+
+
+       ----------------------------------------------------------------------------
+       -- PS2 signal conditioning
+
+       -- Strengthen input signals ('Z','H' -> '1') for simulation
+       ps2_clk_int  <= '0' when ps2_clk  = '0' else '1';
+       ps2_data_int <= '0' when ps2_data = '0' else '1';
+
+       -- Detect rising and falling edges
+       process (rst_i, clk_i, edge_detector_reg, ps2_clk_int)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               edge_detector_reg <= (others => '1');
+                       else
+                               edge_detector_reg <= edge_detector_reg(edge_detector_reg'high-1 downto 0) & ps2_clk_int;
+                       end if;
+               end if;
+       end process;
+       ps2_falling_edge <= '1' when edge_detector_reg = "10" else '0';
+       ps2_rising_edge  <= '1' when edge_detector_reg = "01" else '0';
+
+       -- Weaken output signals to allow device to pull low
+       ps2_clk  <= '0' when host_clk  = '0' else 'Z';
+       ps2_data <= '0' when host_data = '0' else 'Z';
+
+end behavioral;
diff --git a/libraries/ps2/ps2_host_wb.vhd b/libraries/ps2/ps2_host_wb.vhd
new file mode 100644 (file)
index 0000000..dbd0eac
--- /dev/null
@@ -0,0 +1,297 @@
+--------------------------------------------------------------------------------
+-- TODO: https://eecs.umich.edu/courses/doing_dsp/handout/SRL16E.pdf
+--       Play with using shift-registers to make this more dense
+--
+--    +---+---+---+---+---+---+---+---+
+--    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+--    +---+---+---+---+---+---+---+---+
+-- 0  |INH|AIH|                   |ENA| CTRL
+--    +---+---+---+---+---+---+---+---+
+-- 1  |OER|FER|PER|   |TXE|TXR|RXF|RXR| IMASK
+--    +---+---+---+---+---+---+---+---+
+-- 2  |OER|FER|PER|   |TXE|TXR|RXF|RXR| IFLAG (W1C)
+--    +---+---+---+---+---+---+---+---+
+-- 3  |                   |TTO|RTO|NCK| ERROR (RO)
+--    +-------------------+---+---+---+
+-- 4  |             DATA              | DATA
+--    +-------------------------------+
+-- 5  |          (RESERVED)           |
+--    +-------------------------------+
+-- 6  |          (RESERVED)           |
+--    +-------------------------------+
+-- 7  |          (RESERVED)           |
+--    +-------------------------------+
+--
+-- CTRL register bits:
+-- ENA - Enable interface, interface and FIFOs are cleared and reset when 0
+-- AIH - Auto-inhibit device from sending if FIFO is full
+-- INH - Inhibit device from sending
+--
+-- IMASK/IFLAG register bits:
+-- RXR - Receiver has at least one byte ready
+--       Deasserts when all DATA read
+-- RXF - Receiver FIFO is full
+--       Deasserts on read from DATA
+-- TXR - Transmitter ready to accept at least one byte
+--       Deasserts on when TX FIFO is full
+-- TXE - Transmitter FIFO is empty
+--       Deasserts on write to DATA
+-- PER - Receiver parity error
+--       Write one to clear
+-- FER - Receiver framing error, either start or stop bit was incorrect
+--       Write one to clear
+-- OER - Receiver FIFO overrun, at least one byte was lost due to lack of space
+--       Write one to clear
+--
+-- ERROR register bits, cleared by writing ENA to zero:
+-- TTO - Transmitter timeout error, PS2 clock stopped unexpectedly
+-- RTO - Receiver timeout error, PS2 clock stopped unexpectedly
+-- NER - Transmitter NACK error, at least one byte was not acknowledged
+--
+-- Notes:
+--
+-- It's a good idea to always use the auto-inhibit feature, as it allows the
+-- device to be informed of and prevent FIFO spillage, possibly retransmitting
+-- or performing more queueing on its end.
+--
+-- User must check flags before reading/writing DATA register to ensure data is
+-- available in RX or space is available in TX, otherwise undefined data will be
+-- returned or written data will be lost.
+--
+-- TODO: Add timeout/start bit timer divisors
+-- TODO: Use generics for FIFO capacities
+-- TODO: Use constants for register addresses to allow easy rearranging later
+--
+-- FIXME: Missing OER signal (probably framing and parity errors too)
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+
+library utility;
+library work;
+
+
+entity ps2_host_wb is
+       port (
+               rst_i:       in    std_logic;
+               clk_i:       in    std_logic;
+
+               cyc_i:       in    std_logic;
+               stb_i:       in    std_logic;
+               we_i:        in    std_logic;
+               ack_o:       out   std_logic;
+               adr_i:       in    std_logic_vector(2 downto 0);
+               dat_i:       in    std_logic_vector(7 downto 0);
+               dat_o:       out   std_logic_vector(7 downto 0);
+
+               rx_ready:    out   std_logic;
+               rx_full:     out   std_logic;
+               tx_ready:    out   std_logic;
+               tx_empty:    out   std_logic;
+
+               err_nack:    out   std_logic;
+               err_txto:    out   std_logic;
+               err_rxto:    out   std_logic;
+               err_missed:  out   std_logic;
+               err_parity:  out   std_logic;
+               err_framing: out   std_logic;
+
+               ps2_clk:     inout std_logic;
+               ps2_data:    inout std_logic
+       );
+end ps2_host_wb;
+
+
+architecture behavioral of ps2_host_wb is
+
+       -- Bus-exposed registers
+       signal ctrl_reg:       std_logic_vector(7 downto 0);
+       signal imask_reg:      std_logic_vector(7 downto 0);
+       signal iflag_reg:      std_logic_vector(7 downto 0);
+       signal error_vec:      std_logic_vector(7 downto 0);
+
+       -- Control register bit signals
+       signal ctrl_ena:       std_logic;
+       signal ctrl_inhibit:   std_logic;
+       signal ctrl_auto_inh:  std_logic;
+
+       -- Interface connections
+       signal iface_rst:      std_logic;
+       signal tx_err_nack:    std_logic;
+       signal tx_err_timeout: std_logic;
+       signal rx_err_timeout: std_logic;
+       signal rx_err_missed:  std_logic;
+       signal rx_err_parity:  std_logic;
+       signal rx_err_start:   std_logic;
+       signal rx_err_stop:    std_logic;
+
+       -- Transmit FIFO connections
+       signal txqt_stb:       std_logic;
+       signal txqt_ack:       std_logic;
+       signal txqt_dat:       std_logic_vector(7 downto 0);
+       signal txqh_stb:       std_logic;
+       --     txqh_ack        left unconnected to prevent wb bus stalls when full
+       --     txqh_dat        comes from dat_i
+       signal txq_empty:      std_logic;
+       signal txq_full:       std_logic;
+
+       -- Receive FIFO connections
+       signal rxqh_stb:       std_logic;
+       signal rxqh_ack:       std_logic;
+       signal rxqh_dat:       std_logic_vector(7 downto 0);
+       signal rxqt_stb:       std_logic;
+       signal rxqt_ack:       std_logic;
+       signal rxqt_dat:       std_logic_vector(7 downto 0);
+       signal rxq_empty:      std_logic;
+       signal rxq_full:       std_logic;
+
+       signal data_stb:       std_logic;
+
+begin
+
+       -- Wishbone interface
+       process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               ctrl_reg  <= (others => '0');
+                               imask_reg <= (others => '0');
+                               iflag_reg <= (others => '0');
+                       else
+                               -- Perform write cycle
+                               if cyc_i = '1' and stb_i = '1' and we_i = '1' then
+                                       case adr_i is
+                                               when "000"  => ctrl_reg  <= dat_i;
+                                               when "001"  => imask_reg <= dat_i;
+                                               when "010"  => iflag_reg <= iflag_reg and (not dat_i);  -- W1C
+                                               when "011"  => null;  -- RO
+                                               when "100"  => null;  -- Attached to FIFOs
+                                               when others => null;
+                                       end case;
+                               end if;
+
+                               -- Latch interrupt flags
+                               iflag_reg(0) <= rxqt_stb;  -- using !rxq_empty will trigger interrupt before data makes it through the pipeline FIFO
+                               iflag_reg(1) <= rxq_full;
+                               iflag_reg(2) <= not txq_full;
+                               iflag_reg(3) <= txq_empty;
+
+                               -- Catch receive error flags when the interface reports a new byte
+                               if rxqh_stb = '1' then
+                                       iflag_reg(5) <= iflag_reg(5) or rx_err_parity;
+                                       iflag_reg(6) <= iflag_reg(6) or rx_err_start or rx_err_stop;
+                                       iflag_reg(7) <= iflag_reg(7) or rx_err_missed;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       error_vec <= "00000" & tx_err_timeout & rx_err_timeout & tx_err_nack;
+
+       with adr_i select dat_o <=
+               ctrl_reg        when "000",
+               imask_reg       when "001",
+               iflag_reg       when "010",
+               error_vec       when "011",
+               rxqt_dat        when "100",
+               (others => '1') when others;
+
+       ack_o <= '1';
+
+       -- Interrupt output signals
+       rx_ready      <= iflag_reg(0) and imask_reg(0);
+       rx_full       <= iflag_reg(1) and imask_reg(1);
+       tx_ready      <= iflag_reg(2) and imask_reg(2);
+       tx_empty      <= iflag_reg(3) and imask_reg(3);
+
+       err_missed    <= iflag_reg(5) and imask_reg(5);
+       err_framing   <= iflag_reg(6) and imask_reg(6);
+       err_parity    <= iflag_reg(7) and imask_reg(7);
+
+       err_nack      <= tx_err_nack;
+       err_rxto      <= rx_err_timeout;
+       err_txto      <= tx_err_timeout;
+
+       -- Control register bit signals
+       ctrl_inhibit  <= ctrl_reg(7);
+       ctrl_auto_inh <= ctrl_reg(6);
+       ctrl_ena      <= ctrl_reg(0);
+
+       data_stb      <= (cyc_i and stb_i) when adr_i = "100" else '0';
+       txqh_stb      <= data_stb when we_i = '1' else '0';
+       rxqt_ack      <= data_stb when we_i = '0' else '0';
+
+       iface_rst     <= rst_i or (not ctrl_ena);
+
+       e_txq: entity utility.fifo
+               generic map (
+                       WIDTH    => 8,
+                       COUNT    => 4
+               )
+               port map (
+                       rst_i    => iface_rst,
+                       clk_i    => clk_i,
+
+                       h_stb_i  => txqh_stb,
+                       h_ack_o  => open,
+                       h_dat_i  => dat_i,
+
+                       t_stb_o  => txqt_stb,
+                       t_ack_i  => txqt_ack,
+                       t_dat_o  => txqt_dat,
+
+                       is_empty => txq_empty,
+                       is_full  => txq_full
+               );
+
+       e_rxq: entity utility.fifo
+               generic map (
+                       WIDTH    => 8,
+                       COUNT    => 4
+               )
+               port map (
+                       rst_i    => iface_rst,
+                       clk_i    => clk_i,
+
+                       h_stb_i  => rxqh_stb,
+                       h_ack_o  => rxqh_ack,
+                       h_dat_i  => rxqh_dat,
+
+                       t_stb_o  => rxqt_stb,
+                       t_ack_i  => rxqt_ack,
+                       t_dat_o  => rxqt_dat,
+
+                       is_empty => rxq_empty,
+                       is_full  => rxq_full
+               );
+
+       e_ps2: entity work.ps2_host
+               port map (
+                       rst_i          => iface_rst,
+                       clk_i          => clk_i,
+
+                       tx_stb_i       => txqt_stb,
+                       tx_ack_o       => txqt_ack,
+                       tx_dat_i       => txqt_dat,
+
+                       rx_stb_o       => rxqh_stb,
+                       rx_ack_i       => rxqh_ack,
+                       rx_dat_o       => rxqh_dat,
+
+                       inhibit        => ctrl_inhibit,
+                       auto_inhibit   => ctrl_auto_inh,
+
+                       tx_err_nack    => tx_err_nack,
+                       tx_err_timeout => tx_err_timeout,
+                       rx_err_timeout => rx_err_timeout,
+                       rx_err_missed  => rx_err_missed,
+                       rx_err_parity  => rx_err_parity,
+                       rx_err_start   => rx_err_start,
+                       rx_err_stop    => rx_err_stop,
+
+                       ps2_clk        => ps2_clk,
+                       ps2_data       => ps2_data
+               );
+
+end behavioral;
diff --git a/libraries/ps2/tests/nexys2.vhd b/libraries/ps2/tests/nexys2.vhd
new file mode 100644 (file)
index 0000000..da3a957
--- /dev/null
@@ -0,0 +1,98 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+use ieee.numeric_std.all;
+
+library nexys2_lib;
+library work;
+
+
+entity nexys2 is
+       port (
+               clk_50: in    std_logic;
+
+               -- 7-segment display
+               seg:    out   std_logic_vector(6 downto 0);
+               dp:     out   std_logic;
+               an:     out   std_logic_vector(3 downto 0);
+
+               -- Misc
+               Led:    out   std_logic_vector(7 downto 0);
+               sw:     in    std_logic_vector(7 downto 0);
+               btn:    in    std_logic_vector(3 downto 0);
+
+               -- PS2 serial
+               PS2C:   inout std_logic;
+               PS2D:   inout std_logic
+       );
+end nexys2;
+
+
+architecture behavioral of nexys2 is
+
+       -- Wishbone SYSCON
+       signal clk:      std_logic;
+       signal rst:      std_logic;
+
+       -- Wishbone bus
+       signal cyc:      std_logic;
+       signal stb:      std_logic;
+       signal we:       std_logic;
+       signal ack:      std_logic;
+       signal adr:      std_logic_vector(7 downto 0);
+       signal dat_mosi: std_logic_vector(7 downto 0);
+       signal dat_miso: std_logic_vector(7 downto 0);
+
+begin
+
+       e_ps2_host: entity work.ps2_host_wb
+               port map (
+                       clk_i       => clk,
+                       rst_i       => rst,
+
+                       cyc_i       => cyc,
+                       stb_i       => stb,
+                       we_i        => we,
+                       ack_o       => ack,
+                       adr_i       => adr(2 downto 0),
+                       dat_i       => dat_mosi,
+                       dat_o       => dat_miso,
+
+                       rx_ready    => Led(0),
+                       rx_full     => Led(1),
+                       tx_ready    => Led(2),
+                       tx_empty    => Led(3),
+
+                       err_nack    => open,
+                       err_txto    => open,
+                       err_rxto    => Led(7),
+                       err_missed  => Led(6),
+                       err_parity  => Led(5),
+                       err_framing => Led(4),
+
+                       ps2_clk     => PS2C,
+                       ps2_data    => PS2D
+               );
+
+       e_manual_wb: entity nexys2_lib.wishbone_debugger
+               port map (
+                       clk_o  => clk,
+                       rst_o  => rst,
+
+                       cyc_o  => cyc,
+                       stb_o  => stb,
+                       we_o   => we,
+                       ack_i  => ack,
+                       adr_o  => adr,
+                       dat_o  => dat_mosi,
+                       dat_i  => dat_miso,
+
+                       clk_50 => clk_50,
+                       seg    => seg,
+                       dp     => dp,
+                       an     => an,
+                       sw     => sw,
+                       btn    => btn
+               );
+
+end behavioral;
diff --git a/libraries/ps2/tests/program.sh b/libraries/ps2/tests/program.sh
new file mode 100755 (executable)
index 0000000..14d6689
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog
+#djtgcfg -d Nexys2 -i 0 -f OnBoardMemCfg.bit prog
+
diff --git a/libraries/ps2/tests/test_ps2_host.vhd b/libraries/ps2/tests/test_ps2_host.vhd
new file mode 100644 (file)
index 0000000..3ed07e0
--- /dev/null
@@ -0,0 +1,220 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library work;
+library simulated;
+
+
+entity test_ps2_host is
+end test_ps2_host;
+
+
+architecture behavior of test_ps2_host is
+
+       constant clk_i_period: time := 10 ns;
+
+       signal rst_i:          std_logic;
+       signal clk_i:          std_logic;
+
+       signal tx_stb_i:       std_logic;
+       signal tx_ack_o:       std_logic;
+       signal tx_dat_i:       std_logic_vector(7 downto 0);
+
+       signal rx_stb_o:       std_logic;
+       signal rx_ack_i:       std_logic;
+       signal rx_dat_o:       std_logic_vector(7 downto 0);
+
+       signal inhibit:        std_logic;
+       signal auto_inhibit:   std_logic;
+
+       signal tx_err_nack:    std_logic;
+       signal tx_err_timeout: std_logic;
+       signal rx_err_timeout: std_logic;
+       signal rx_err_missed:  std_logic;
+       signal rx_err_parity:  std_logic;
+       signal rx_err_start:   std_logic;
+       signal rx_err_stop:    std_logic;
+
+       signal tx_byte:        std_logic_vector(7 downto 0);
+
+       signal ps2_clk:        std_logic;
+       signal ps2_data:       std_logic;
+
+begin
+
+       test: process
+       begin
+               -- Initial values
+               tx_stb_i     <= '0';
+               tx_dat_i     <= x"00";
+               rx_ack_i     <= '0';
+               inhibit      <= '0';
+               auto_inhibit <= '0';
+
+               -- Reset
+               rst_i <= '1';
+               wait for clk_i_period;
+               rst_i <= '0';
+               wait for clk_i_period;
+
+               wait for 200 us;
+
+               -- Transmit to device normally
+               tx_stb_i <= '1';
+               tx_dat_i <= x"a5";
+               wait until rising_edge(clk_i);
+               while tx_ack_o = '0' loop
+                       wait until rising_edge(clk_i);
+               end loop;
+               tx_stb_i <= '0';
+               wait for 1 ms;
+               assert tx_err_nack    = '0' report "NACK"       severity error;
+               assert tx_err_timeout = '0' report "TX timeout" severity error;
+
+               -- Receive from device normally
+               tx_byte <= x"c3";
+               wait until rx_stb_o = '1';
+               assert rx_err_timeout = '0' report "RX timeout" severity error;
+               assert rx_err_missed  = '0' report "RX missed"  severity error;
+               assert rx_err_parity  = '0' report "RX parity"  severity error;
+               assert rx_err_start   = '0' report "RX start"   severity error;
+               assert rx_err_stop    = '0' report "RX stop"    severity error;
+               rx_ack_i <= '1';
+               wait until rising_edge(clk_i);
+               rx_ack_i <= '0';
+
+               wait for 200 us;
+
+               -- Attempt to transmit while transmitting (waits until finished)
+               wait until falling_edge(clk_i);
+               tx_stb_i <= '1';
+               tx_dat_i <= x"12";
+               wait until rising_edge(clk_i);
+               tx_stb_i <= '0';
+               wait for 400 us;
+               tx_stb_i <= '1';
+               tx_dat_i <= x"34";
+               wait until rising_edge(clk_i);
+               wait until rising_edge(clk_i);
+               assert tx_ack_o = '0' report "TX accepted extra byte" severity error;
+               wait until tx_ack_o = '1';
+               wait until rising_edge(clk_i);
+               tx_stb_i <= '0';
+               wait for 1 ms;
+
+               -- Attempt to transmit while receiving (waits until finished)
+               tx_byte <= x"56";
+               wait for 400 us;
+               wait until falling_edge(clk_i);
+               tx_stb_i <= '1';
+               tx_dat_i <= x"78";
+               wait until rx_stb_o = '1';
+               wait for 200 us;
+               wait until falling_edge(clk_i);
+               rx_ack_i <= '1';
+               wait until rising_edge(clk_i);
+               wait until rising_edge(clk_i);
+               tx_stb_i <= '0';
+               wait for 1 ms;
+
+               -- Assert inhibit during transmit (finishes transmit)
+               wait until falling_edge(clk_i);
+               tx_stb_i <= '1';
+               tx_dat_i <= x"9a";
+               wait until rising_edge(clk_i);
+               tx_stb_i <= '0';
+               wait for 400 us;
+               inhibit <= '1';
+               wait for 1 ms;
+
+               -- Attempt to transmit while inhibited (waits until uninhibited)
+               wait until falling_edge(clk_i);
+               tx_stb_i <= '1';
+               tx_dat_i <= x"bc";
+               wait until rising_edge(clk_i);
+               assert tx_ack_o = '0' report "TX ACKed while inhibited" severity error;
+               wait for 200 us;
+               inhibit <= '0';
+               wait until tx_ack_o = '1';
+               wait until rising_edge(clk_i);
+               tx_stb_i <= '0';
+               wait for 1 ms;
+
+               -- Inhibit while receiving aborts device
+               tx_byte <= x"de";
+               wait for 400 us;
+               inhibit <= '1';
+               wait for 200 us;
+               inhibit <= '0';
+               wait for 400 us;
+
+               -- Inhibit during stop bit waits to finish then inhibits
+               tx_byte <= x"f0";
+               wait for 620 us;  -- This should put it into the 11th clock
+               inhibit <= '1';
+               wait until rx_stb_o = '1';
+               rx_ack_i <= '1';
+               wait until rising_edge(clk_i);
+               rx_ack_i <= '0';
+               inhibit <= '0';
+               wait for 400 us;
+
+               -- Auto-inhibit inhibits device after reception until acknowledgement
+               auto_inhibit <= '1';
+               tx_byte <= x"41";
+               wait for 1 ms;
+               assert rx_stb_o = '1';
+               assert ps2_clk = '0';
+               wait until falling_edge(clk_i);
+               rx_ack_i <= '1';
+               wait until rising_edge(clk_i);
+               rx_ack_i <= '0';
+               wait for 200 us;
+
+               wait;
+       end process;
+
+
+       uut: entity work.ps2_host
+               port map (
+                       rst_i          => rst_i,
+                       clk_i          => clk_i,
+                       tx_stb_i       => tx_stb_i,
+                       tx_ack_o       => tx_ack_o,
+                       tx_dat_i       => tx_dat_i,
+                       rx_stb_o       => rx_stb_o,
+                       rx_ack_i       => rx_ack_i,
+                       rx_dat_o       => rx_dat_o,
+                       inhibit        => inhibit,
+                       auto_inhibit   => auto_inhibit,
+                       tx_err_nack    => tx_err_nack,
+                       tx_err_timeout => tx_err_timeout,
+                       rx_err_timeout => rx_err_timeout,
+                       rx_err_missed  => rx_err_missed,
+                       rx_err_parity  => rx_err_parity,
+                       rx_err_start   => rx_err_start,
+                       rx_err_stop    => rx_err_stop,
+                       ps2_clk        => ps2_clk,
+                       ps2_data       => ps2_data
+               );
+
+       device: entity simulated.ps2_device
+               port map (
+                       tx_byte  => tx_byte,
+
+                       ps2_clk  => ps2_clk,
+                       ps2_data => ps2_data
+               );
+
+       ps2_clk  <= 'H';
+       ps2_data <= 'H';
+
+       clk_i_process :process
+       begin
+               clk_i <= '0';
+               wait for clk_i_period/2;
+               clk_i <= '1';
+               wait for clk_i_period/2;
+       end process;
+
+end;
diff --git a/libraries/ps2/tests/test_ps2_host_wb.vhd b/libraries/ps2/tests/test_ps2_host_wb.vhd
new file mode 100644 (file)
index 0000000..643005b
--- /dev/null
@@ -0,0 +1,312 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library simulated;
+
+
+entity test_ps2_host_wb is
+end test_ps2_host_wb;
+
+
+architecture behavior of test_ps2_host_wb is
+
+       signal rst_i:       std_logic;
+       signal clk_i:       std_logic;
+
+       signal cyc_i:       std_logic;
+       signal stb_i:       std_logic;
+       signal we_i:        std_logic;
+       signal ack_o:       std_logic;
+       signal adr_i:       std_logic_vector(2 downto 0);
+       signal dat_i:       std_logic_vector(7 downto 0);
+       signal dat_o:       std_logic_vector(7 downto 0);
+
+       signal rx_ready:    std_logic;
+       signal rx_full:     std_logic;
+       signal tx_ready:    std_logic;
+       signal tx_empty:    std_logic;
+
+       signal err_nack:    std_logic;
+       signal err_txto:    std_logic;
+       signal err_rxto:    std_logic;
+       signal err_missed:  std_logic;
+       signal err_parity:  std_logic;
+       signal err_framing: std_logic;
+
+       signal ps2_clk:     std_logic;
+       signal ps2_data:    std_logic;
+
+       -- Simulated device interface
+       signal tx_byte:     std_logic_vector(7 downto 0);
+
+       type test_t is (
+               NONE,
+               FIFOVER_S, FIFOVER_R,
+               AUTOHIB
+       );
+       signal test_name: test_t := NONE;
+
+begin
+
+       p_test: process
+       begin
+               -- Initial signal values
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               adr_i <= "000";
+               dat_i <= x"00";
+
+               -- Reset
+               rst_i <= '1';
+               wait for 20 ns;
+               rst_i <= '0';
+
+               -- Unmask signals
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '1';
+               adr_i <= "001";
+               dat_i <= x"ff";
+               wait until rising_edge(clk_i);
+               while ack_o = '0' loop
+                       wait until rising_edge(clk_i);
+               end loop;
+               wait for 10 ns;
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+
+               -- Enable interface
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '1';
+               adr_i <= "000";
+               dat_i <= x"01";
+               wait until rising_edge(clk_i);
+               while ack_o = '0' loop
+                       wait until rising_edge(clk_i);
+               end loop;
+               wait for 10 ns;
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+
+               -- Transmit 0xa5
+               --cyc_i <= '1';
+               --stb_i <= '1';
+               --we_i  <= '1';
+               --adr_i <= "101";
+               --dat_i <= x"a5";
+               --wait until rising_edge(clk_i);
+               --while ack_o = '0' loop
+               --      wait until rising_edge(clk_i);
+               --end loop;
+               --wait for 10 ns;
+               --cyc_i <= '0';
+               --stb_i <= '0';
+               --we_i  <= '0';
+
+               -- Receive 0x1c, 0xf0, 0x1c ('a' keypress)
+               wait for 1 ms;
+               tx_byte <= x"1c";
+               wait for 800 us;
+               tx_byte <= x"f0";
+               wait for 800 us;
+               tx_byte <= x"1c";
+               wait for 1 ms;
+
+               -- Retrieve keypresses
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "100";
+               wait until rising_edge(clk_i);
+               wait until falling_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               wait for 10 us;
+
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "100";
+               wait until rising_edge(clk_i);
+               wait until falling_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               wait for 10 us;
+
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "100";
+               wait until rising_edge(clk_i);
+               wait until falling_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               wait for 10 us;
+               
+               wait for 200 us;
+
+               -- Receive just too damn many things and overflow the FIFO
+               test_name <= FIFOVER_S;
+               tx_byte <= x"12";
+               wait for 800 us;
+               tx_byte <= x"34";
+               wait for 800 us;
+               tx_byte <= x"56";
+               wait for 800 us;
+               tx_byte <= x"78";
+               wait for 800 us;
+               tx_byte <= x"9a";
+               wait for 1 ms;
+
+               -- Retrieve keypresses
+               test_name <= FIFOVER_R;
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "100";
+               wait until rising_edge(clk_i);
+               wait until falling_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               wait for 10 us;
+
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "100";
+               wait until rising_edge(clk_i);
+               wait until falling_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               wait for 10 us;
+
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "100";
+               wait until rising_edge(clk_i);
+               wait until falling_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               wait for 10 us;
+
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "100";
+               wait until rising_edge(clk_i);
+               wait until falling_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               wait for 10 us;
+
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '0';
+               adr_i <= "100";
+               wait until rising_edge(clk_i);
+               wait until falling_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               wait for 10 us;
+
+               wait for 200 us;
+
+               -- Test auto-inhibit
+               test_name <= AUTOHIB;
+
+               wait until falling_edge(clk_i);
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '1';
+               adr_i <= "000";
+               dat_i <= x"41";
+               wait until rising_edge(clk_i);
+               while ack_o = '0' loop
+                       wait until rising_edge(clk_i);
+               end loop;
+               wait for 10 ns;
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               wait for 200 us;
+
+               tx_byte <= x"12";
+               wait for 800 us;
+               tx_byte <= x"34";
+               wait for 800 us;
+               tx_byte <= x"56";
+               wait for 800 us;
+               tx_byte <= x"78";
+               wait for 800 us;
+               tx_byte <= x"9a";
+               wait for 1 ms;
+
+               wait;
+       end process;
+
+       -- Simulated device
+       e_simdev: entity simulated.ps2_device
+               port map (
+                       tx_byte  => tx_byte,
+
+                       ps2_clk  => ps2_clk,
+                       ps2_data => ps2_data
+               );
+
+       -- Pull-up resistors
+       ps2_clk  <= 'H';
+       ps2_data <= 'H';
+
+       e_uut: entity work.ps2_host_wb
+               port map (
+                       rst_i       => rst_i,
+                       clk_i       => clk_i,
+
+                       cyc_i       => cyc_i,
+                       stb_i       => stb_i,
+                       we_i        => we_i,
+                       ack_o       => ack_o,
+                       adr_i       => adr_i,
+                       dat_i       => dat_i,
+                       dat_o       => dat_o,
+
+                       rx_ready    => rx_ready,
+                       rx_full     => rx_full,
+                       tx_ready    => tx_ready,
+                       tx_empty    => tx_empty,
+
+                       err_nack    => err_nack,
+                       err_txto    => err_txto,
+                       err_rxto    => err_rxto,
+                       err_missed  => err_missed,
+                       err_parity  => err_parity,
+                       err_framing => err_framing,
+
+                       ps2_clk     => ps2_clk,
+                       ps2_data    => ps2_data
+               );
+
+       p_clk: process
+       begin
+               clk_i <= '0';
+               wait for 10 ns;
+               clk_i <= '1';
+               wait for 10 ns;
+       end process;
+
+end;
diff --git a/libraries/rs232/rs232_rx.vhd b/libraries/rs232/rs232_rx.vhd
new file mode 100644 (file)
index 0000000..5090052
--- /dev/null
@@ -0,0 +1,231 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_misc.all;
+
+
+entity rs232_rx is
+       port (
+               rst_i:      in  std_logic;
+               clk_i:      in  std_logic;
+
+               stb_o:      out std_logic;
+               ack_i:      in  std_logic;
+               dat_o:      out std_logic_vector(7 downto 0);
+
+               divisor_in: in  std_logic_vector(15 downto 0);
+               parity_in:  in  std_logic;  -- Expect a parity bit
+
+               missed_out: out std_logic;
+               parity_out: out std_logic;
+
+               rx_in:      in  std_logic
+       );
+end rs232_rx;
+
+
+architecture behavioral of rs232_rx is
+
+       type state_t is (S_READY, S_START, S_DATA, S_PARITY, S_STOP);
+       signal state_reg:      state_t;
+       signal next_state:     state_t;
+
+       signal latch_en:       std_logic;
+       signal ready_out_reg:  std_logic;
+       signal missed_out_reg: std_logic;
+       signal parity_out_reg: std_logic;
+       signal data_out_reg:   std_logic_vector(7 downto 0);
+
+       signal bit_count_done: std_logic;
+       signal bit_count_reg:  unsigned(3 downto 0);
+       signal shift_en:       std_logic;
+       signal shifter_reg:    std_logic_vector(7 downto 0);
+       signal parity_en:      std_logic;
+       signal parity_reg:     std_logic;
+
+       signal baud_gen_en:    std_logic;
+       signal baud_counter:   unsigned(15 downto 0);
+       signal next_bit:       std_logic;
+       signal half_bit:       std_logic;
+
+begin
+
+       ----------------------------------------------------------------------------
+       -- State machine
+
+       process (rst_i, clk_i, next_state)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               state_reg <= S_READY;
+                       else
+                               state_reg <= next_state;
+                       end if;
+               end if;
+       end process;
+
+       process (state_reg, rx_in, parity_in, half_bit, next_bit, bit_count_done)
+       begin
+               -- Default to remaining in current state
+               next_state  <= state_reg;
+
+               -- Default to idling other hardware
+               shift_en    <= '0';
+               parity_en   <= '0';
+               baud_gen_en <= '0';
+               latch_en    <= '0';
+
+               case state_reg is
+                       when S_READY =>
+                               if rx_in = '0' then
+                                       next_state <= S_START;
+                               end if;
+
+                       when S_START =>
+                               baud_gen_en <= '1';
+                               if next_bit = '1' then
+                                       next_state <= S_DATA;
+                               end if;
+
+                       when S_DATA =>
+                               baud_gen_en <= '1';
+                               shift_en    <= '1';
+                               if next_bit = '1' and bit_count_done = '1' then
+                                       if parity_in = '1' then
+                                               next_state <= S_PARITY;
+                                       else
+                                               latch_en <= '1';
+                                               next_state <= S_STOP;
+                                       end if;
+                               end if;
+
+                       when S_PARITY =>
+                               baud_gen_en <= '1';
+                               parity_en <= '1';
+                               if next_bit = '1' then
+                                       latch_en <= '1';
+                                       next_state <= S_STOP;
+                               end if;
+
+                       when S_STOP =>
+                               baud_gen_en <= '1';
+                               if next_bit = '1' then
+                                       next_state <= S_READY;
+                               end if;
+
+                       when others =>
+                               next_state <= S_READY;
+               end case;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Control interface
+
+       process (rst_i, clk_i, ack_i, latch_en, ready_out_reg, parity_reg, shifter_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               missed_out_reg <= '0';
+                               ready_out_reg  <= '0';
+                               parity_out_reg <= '0';
+                               data_out_reg   <= (others => '0');
+                       else
+                               if ack_i = '1' then
+                                       missed_out_reg <= '0';
+                                       ready_out_reg  <= '0';
+                               end if;
+
+                               -- Separate if statement so a new value can be latched during
+                               -- the same cycle that a value is acknowledged
+                               if latch_en = '1' then
+                                       missed_out_reg <= ready_out_reg;
+                                       ready_out_reg  <= '1';
+                                       parity_out_reg <= parity_reg;
+                                       data_out_reg   <= shifter_reg;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       stb_o  <= ready_out_reg;
+       missed_out <= missed_out_reg;
+       parity_out <= parity_out_reg;
+       dat_o   <= data_out_reg;
+
+
+       ----------------------------------------------------------------------------
+       -- Shift register, bit counter, and parity checker
+
+       process (rst_i, clk_i, shift_en, half_bit, rx_in)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               shifter_reg <= (others => '0');
+                       else
+                               if shift_en = '1' and half_bit = '1' then
+                                       shifter_reg <= rx_in & shifter_reg(shifter_reg'high downto 1);
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       process (rst_i, clk_i, latch_en, shift_en, parity_en, half_bit, rx_in, parity_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               parity_reg <= '0';
+                       else
+                               if latch_en = '1' then
+                                       parity_reg <= '0';
+                               elsif (shift_en = '1' or parity_en = '1') and half_bit = '1' then
+                                       parity_reg <= parity_reg xor rx_in;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       process (rst_i, clk_i, shift_en, bit_count_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               bit_count_reg <= x"8";
+                       else
+                               if shift_en = '0' then
+                                       bit_count_reg <= x"8";
+                               elsif half_bit = '1' then
+                                       bit_count_reg <= bit_count_reg - 1;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       bit_count_done <= '1' when or_reduce(std_logic_vector(bit_count_reg)) = '0' else '0';
+
+
+       ----------------------------------------------------------------------------
+       -- Baud rate generator and bit timer
+
+       baud_gen: process (rst_i, clk_i, baud_gen_en, next_bit)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               baud_counter <= (others => '0');
+                       else
+                               if baud_gen_en = '1' then
+                                       if next_bit = '1' then
+                                               baud_counter <= (others => '0');
+                                       else
+                                               baud_counter <= baud_counter + 1;
+                                       end if;
+                               else
+                                       baud_counter <= (others => '0');
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       next_bit <= '1' when std_logic_vector(baud_counter) = divisor_in else '0';
+       half_bit <= '1' when std_logic_vector(baud_counter) = '0' & divisor_in(divisor_in'high downto 1) else '0';
+
+end behavioral;
diff --git a/libraries/rs232/rs232_tx.vhd b/libraries/rs232/rs232_tx.vhd
new file mode 100644 (file)
index 0000000..7aa872a
--- /dev/null
@@ -0,0 +1,207 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_misc.all;
+
+
+entity rs232_tx is
+       port (
+               rst_i:         in  std_logic;
+               clk_i:         in  std_logic;
+
+               stb_i:         in  std_logic;
+               ack_o:         out std_logic;
+               dat_i:         in  std_logic_vector(7 downto 0);
+
+               parity_en_in:  in  std_logic;
+               parity_odd_in: in  std_logic;
+               stop_bits_in:  in  std_logic;  -- '0'->1 stop bit, '1'->2 stop bits
+               divisor_in:    in  std_logic_vector(15 downto 0);
+
+               tx_out:        out std_logic
+       );
+end rs232_tx;
+
+
+architecture behavioral of rs232_tx is
+
+       type state_t is (S_READY, S_START, S_DATA, S_PARITY, S_STOP_1, S_STOP_2);
+       signal state_reg:      state_t;
+       signal next_state:     state_t;
+
+       signal bit_count_done: std_logic;
+       signal bit_count_reg:  unsigned(3 downto 0);
+       signal shift_en:       std_logic;
+       signal shifter_reg:    std_logic_vector(7 downto 0);
+       signal parity_reg:     std_logic;
+
+       signal baud_gen_en:    std_logic;
+       signal baud_counter:   unsigned(15 downto 0);
+       signal next_bit:       std_logic;
+
+begin
+
+       ----------------------------------------------------------------------------
+       -- State machine
+
+       process (rst_i, clk_i, next_state)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               state_reg <= S_READY;
+                       else
+                               state_reg <= next_state;
+                       end if;
+               end if;
+       end process;
+
+       process (state_reg, stb_i, parity_en_in, next_bit, bit_count_done, shifter_reg, parity_reg, stop_bits_in)
+       begin
+               -- Default to remaining in current state
+               next_state <= state_reg;
+
+               -- Default to idling other hardware
+               shift_en <= '0';
+               baud_gen_en <= '0';
+
+               -- Default to idle tx line output
+               tx_out <= '1';
+
+               -- Default to not ready
+               ack_o <= '0';
+
+               case state_reg is
+                       when S_READY =>
+                               ack_o <= '1';
+                               if stb_i = '1' then
+                                       next_state <= S_START;
+                               end if;
+
+                       when S_START =>
+                               baud_gen_en <= '1';
+                               tx_out <= '0';
+                               if next_bit = '1' then
+                                       next_state <= S_DATA;
+                               end if;
+
+                       when S_DATA =>
+                               baud_gen_en <= '1';
+                               shift_en <= '1';
+                               tx_out <= shifter_reg(0);
+                               if next_bit = '1' and bit_count_done = '1' then
+                                       if parity_en_in = '1' then
+                                               next_state <= S_PARITY;
+                                       else
+                                               if stop_bits_in = '1' then
+                                                       next_state <= S_STOP_1;
+                                               else
+                                                       next_state <= S_STOP_2;
+                                               end if;
+                                       end if;
+                               end if;
+
+                       when S_PARITY =>
+                               baud_gen_en <= '1';
+                               tx_out <= parity_reg;
+                               if next_bit = '1' then
+                                       if stop_bits_in = '1' then
+                                               next_state <= S_STOP_1;
+                                       else
+                                               next_state <= S_STOP_2;
+                                       end if;
+                               end if;
+
+                       when S_STOP_1 =>
+                               baud_gen_en <= '1';
+                               if next_bit = '1' then
+                                       next_state <= S_STOP_2;
+                               end if;
+
+                       when S_STOP_2 =>
+                               baud_gen_en <= '1';
+                               if next_bit = '1' then
+                                       next_state <= S_READY;
+                               end if;
+
+                       when others =>
+                               next_state <= S_READY;
+               end case;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Shift register, bit counter, and parity generator
+
+       process (rst_i, clk_i, stb_i, shift_en, next_bit, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               shifter_reg <= (others => '0');
+                       else
+                               if stb_i = '1' then
+                                       shifter_reg <= dat_i;
+                               elsif shift_en = '1' and next_bit = '1' then
+                                       shifter_reg <= '1' & shifter_reg(shifter_reg'high downto 1);
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       process (rst_i, clk_i, stb_i, parity_odd_in, parity_reg, shifter_reg, shift_en, next_bit)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               parity_reg <= '0';
+                       else
+                               if stb_i = '1' then
+                                       parity_reg <= parity_odd_in;
+                               elsif shift_en = '1' and next_bit = '1' then
+                                       parity_reg <= parity_reg xor shifter_reg(0);
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       process (rst_i, clk_i, shift_en, next_bit)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               bit_count_reg <= x"8";
+                       else
+                               if shift_en = '0' then
+                                       bit_count_reg <= x"8";
+                               elsif next_bit = '1' then
+                                       bit_count_reg <= bit_count_reg - 1;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       bit_count_done <= '1' when bit_count_reg = 0 else '0';
+
+
+       ----------------------------------------------------------------------------
+       -- Baud rate generator and bit timer
+
+       baud_gen: process (rst_i, clk_i, baud_gen_en, next_bit)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               baud_counter <= (others => '0');
+                       else
+                               if baud_gen_en = '1' then
+                                       if next_bit = '1' then
+                                               baud_counter <= (others => '0');
+                                       else
+                                               baud_counter <= baud_counter + 1;
+                                       end if;
+                               else
+                                       baud_counter <= (others => '0');
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       next_bit <= '1' when baud_counter = unsigned(divisor_in) else '0';
+
+end behavioral;
diff --git a/libraries/rs232/rs232_uart.vhd b/libraries/rs232/rs232_uart.vhd
new file mode 100644 (file)
index 0000000..ba6b3e9
--- /dev/null
@@ -0,0 +1,347 @@
+--------------------------------------------------------------------------------
+-- TODO: https://eecs.umich.edu/courses/doing_dsp/handout/SRL16E.pdf
+--       Play with using shift-registers to make this more dense
+--
+--    +---+---+---+---+---+---+---+---+
+--    | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+--    +---+---+---+---+---+---+---+---+
+-- 0  |LB |BRK|   |STP|POD|PE |TXE|RXE| CTRL
+--    +---+-------+---+---+---+---+---+
+-- 1  |       DIVISOR LOW BYTE        | BAUDL
+--    +-------------------------------+
+-- 2  |       DIVISOR HIGH BYTE       | BAUDH
+--    +---+---+---+---+---+---+---+---+
+-- 3  |BER|FER|PER|OER|TXE|TXR|RXF|RXR| IMASK
+--    +---+---+---+---+---+---+---+---+
+-- 4  |BER|FER|PER|OER|TXE|TXR|RXF|RXR| IFLAG
+--    +---+---+---+---+---+---+---+---+
+-- 5  |             DATA              | DATA
+--    +-------------------------------+
+-- 6  |          (RESERVED)           |
+--    +-------------------------------+
+-- 7  |          (RESERVED)           |
+--    +-------------------------------+
+--
+-- CTRL register bits:
+-- RXE - Enable receive logic
+-- TXE - Enable transmit logic
+-- PE  - Enable parity generation and checking
+-- POD - Generate and check for odd parity (else even)
+-- STP - Number of stop bits: clear -> 1, set -> 2
+-- BRK - Create break condition by holding TX line low (not yet implemented)
+-- LB  - Loopback enable
+--
+-- IMASK/IFLAG register bits:
+-- RXR - Receiver has at least one byte ready
+--       Deasserts when all DATA read
+-- RXF - Receiver FIFO is full
+--       Deasserts on read from DATA
+-- TXR - Transmitter ready to accept at least one byte
+--       Deasserts when TX FIFO is full
+-- TXE - Transmitter FIFO is empty
+--       Deasserts on write to DATA
+-- OER - Receiver FIFO overrun, at least one byte was lost due to lack of space
+--       Write one to clear
+-- PER - Receiver parity error
+--       Write one to clear
+-- FER - Receiver framing error, either start or stop bit was incorrect
+--       Write one to clear
+--       (not yet implemented)
+-- BER - Break condition detected (RX line was held low for a full byte)
+--       Write one to clear
+--       (not yet implemented)
+--
+-- BAUDL/BAUDH:
+-- 16-bit clock divisor representing the number of system clock cycles per bit
+--
+-- For 50MHz clock:
+-- +------+---------+
+-- | Baud | Divisor |
+-- +------+---------+
+-- | 9600 | 0x1458  |
+-- +------+---------+
+--
+-- Notes:
+--
+-- User must check flags before reading/writing DATA register to ensure data is
+-- available in RX or space is available in TX, otherwise undefined data will be
+-- returned or written data will be lost.
+--
+-- TODO: Use generics for FIFO capacities
+-- TODO: Fill in baud divisor table for more rates
+-- TODO: Detect and generate break conditions
+-- TODO: Add framing checks
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+
+library utility;
+library work;
+
+
+entity rs232_uart is
+       port (
+               -- Wishbone SYSCON
+               rst_i:        in  std_logic;
+               clk_i:        in  std_logic;
+
+               -- Wishbone bus slave interface
+               cyc_i:        in  std_logic;
+               stb_i:        in  std_logic;
+               we_i:         in  std_logic;
+               ack_o:        out std_logic;
+               adr_i:        in  std_logic_vector(2 downto 0);
+               dat_i:        in  std_logic_vector(7 downto 0);
+               dat_o:        out std_logic_vector(7 downto 0);
+
+               -- Queueing signals
+               rx_ready:     out std_logic;  -- At least one byte ready in the RX FIFO
+               rx_full:      out std_logic;  -- The RX FIFO is full
+               tx_ready:     out std_logic;  -- At least one byte free in the TX FIFO
+               tx_empty:     out std_logic;  -- The TX FIFO is empty
+
+               -- Error signals
+               err_break:    out std_logic;  -- Break condition detected
+               err_framing:  out std_logic;  -- RX stop bit incorrect
+               err_parity:   out std_logic;  -- RX parity incorrect
+               err_overflow: out std_logic;  -- Byte received with no space in queue
+
+               -- RS232 interface
+               tx:           out std_logic;
+               rx:           in  std_logic
+       );
+end rs232_uart;
+
+
+architecture behavioral of rs232_uart is
+
+       -- Register addresses
+       constant ADDR_CTRL:    std_logic_vector(2 downto 0) := "000";
+       constant ADDR_BAUDL:   std_logic_vector(2 downto 0) := "001";
+       constant ADDR_BAUDH:   std_logic_vector(2 downto 0) := "010";
+       constant ADDR_IMASK:   std_logic_vector(2 downto 0) := "011";
+       constant ADDR_IFLAG:   std_logic_vector(2 downto 0) := "100";
+       constant ADDR_DATA:    std_logic_vector(2 downto 0) := "101";
+
+       -- Registers
+       signal ctrl_reg:       std_logic_vector(7 downto 0);
+       signal rx_en:          std_logic;
+       signal tx_en:          std_logic;
+       signal parity_en:      std_logic;
+       signal parity_odd:     std_logic;
+       signal stop_bits:      std_logic;
+       signal do_break:       std_logic;
+       signal loopback:       std_logic;
+
+       signal divisor_reg:    std_logic_vector(15 downto 0);
+
+       signal imask_reg:      std_logic_vector(7 downto 0);
+       signal iflag_reg:      std_logic_vector(7 downto 0);
+
+       signal tx_internal:    std_logic;
+       signal rx_internal:    std_logic;
+
+       signal err_missed_int: std_logic;
+       signal err_parity_int: std_logic;
+
+       signal txqh_stb:       std_logic;
+       signal txqh_ack:       std_logic;
+       --signal txqh_dat:     std_logic;  -- Comes from dat_i
+       signal txqt_stb:       std_logic;
+       signal txqt_ack:       std_logic;
+       signal txqt_dat:       std_logic_vector(7 downto 0);
+       signal txq_empty:      std_logic;
+       signal txq_full:       std_logic;
+
+       signal rxqh_stb:       std_logic;
+       signal rxqh_ack:       std_logic;
+       signal rxqh_dat:       std_logic_vector(7 downto 0);
+       signal rxqt_stb:       std_logic;
+       signal rxqt_ack:       std_logic;
+       signal rxqt_dat:       std_logic_vector(7 downto 0);
+       signal rxq_empty:      std_logic;
+       signal rxq_full:       std_logic;
+
+       signal data_stb:       std_logic;
+       signal rst_tx:         std_logic;
+       signal rst_rx:         std_logic;
+
+begin
+
+       -- Wishbone bus
+       process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               ctrl_reg    <= (others => '0');
+                               divisor_reg <= (others => '0');
+                               imask_reg   <= (others => '0');
+                               iflag_reg   <= (others => '0');
+                       else
+                               if cyc_i = '1' and stb_i = '1' and we_i = '1' then
+                                       case adr_i is
+                                               when ADDR_CTRL  => ctrl_reg <= dat_i;
+                                               when ADDR_BAUDL => divisor_reg( 7 downto 0) <= dat_i;
+                                               when ADDR_BAUDH => divisor_reg(15 downto 8) <= dat_i;
+                                               when ADDR_IMASK => imask_reg <= dat_i;
+                                               when ADDR_IFLAG => iflag_reg <= iflag_reg and (not dat_i);
+                                               when ADDR_DATA  => null;
+                                               when others => null;
+                                       end case;
+                               end if;
+
+                               -- Latch interrupt flags
+                               iflag_reg(0) <= rxqt_stb;  -- using !rxq_empty will trigger interrupt before data makes it through the pipeline FIFO
+                               iflag_reg(1) <= rxq_full;
+                               iflag_reg(2) <= not txq_full;
+                               iflag_reg(3) <= txq_empty;
+
+                               if rxqh_stb = '1' then
+                                       iflag_reg(4) <= iflag_reg(4) or err_missed_int;  -- overflow
+                                       iflag_reg(5) <= iflag_reg(5) or err_parity_int;  -- parity
+                                       --iflag_reg(6) <= iflag_reg(6) or ;  -- framing
+                                       --iflag_reg(7) <= iflag_reg(7) or ;  -- break
+                               end if;
+
+                       end if;
+               end if;
+       end process;
+
+       -- Separate strobe signal for data register access
+       data_stb <= (cyc_i and stb_i) when adr_i = ADDR_DATA else '0';
+
+       -- Deliver data stroe to TX or RX based on transaction direction
+       txqh_stb <= data_stb when we_i = '1' else '0';
+       rxqt_ack <= data_stb when we_i = '0' else '0';
+
+       -- All registers respond to the host immediately
+       -- If no data is available for data reads or no space is available for data
+       -- writes then the transaction gets eaten.  It's up to the user to check the
+       -- flags before attempting data access.
+       ack_o <= '1';
+
+       -- Output multiplexer
+       with adr_i select dat_o <=
+               ctrl_reg                 when ADDR_CTRL,
+               divisor_reg( 7 downto 0) when ADDR_BAUDL,
+               divisor_reg(15 downto 8) when ADDR_BAUDH,
+               imask_reg                when ADDR_IMASK,
+               iflag_reg                when ADDR_IFLAG,
+               rxqt_dat                 when ADDR_DATA,
+               (others => '1')          when others;
+
+       -- Internal control signals
+       rx_en        <= ctrl_reg(0);
+       tx_en        <= ctrl_reg(1);
+       parity_en    <= ctrl_reg(2);
+       parity_odd   <= ctrl_reg(3);
+       stop_bits    <= ctrl_reg(4);
+       do_break     <= ctrl_reg(6);
+       loopback     <= ctrl_reg(7);
+
+       -- Interrupt out signals
+       rx_ready     <= iflag_reg(0) and imask_reg(0);
+       rx_full      <= iflag_reg(1) and imask_reg(1);
+       tx_ready     <= iflag_reg(2) and imask_reg(2);
+       tx_empty     <= iflag_reg(3) and imask_reg(3);
+       err_overflow <= iflag_reg(4) and imask_reg(4);
+       err_parity   <= iflag_reg(5) and imask_reg(5);
+       err_framing  <= iflag_reg(6) and imask_reg(6);
+       err_break    <= iflag_reg(7) and imask_reg(7);
+
+       -- Input-output logic
+       tx <= '1' when tx_en = '0'
+                 else '0' when do_break = '1'
+                          else tx_internal;
+
+       rx_internal <= '1' when rx_en = '0'
+                          else tx_internal when loopback = '1'
+                                           else rx;
+
+       -- Also reset TX/RX logic when each are disabled
+       rst_tx <= rst_i or (not tx_en);
+       rst_rx <= rst_i or (not tx_en);
+
+       -- Transmitter
+       e_tx: entity work.rs232_tx
+               port map (
+                       rst_i         => rst_tx,
+                       clk_i         => clk_i,
+
+                       stb_i         => txqt_stb,
+                       ack_o         => txqt_ack,
+                       dat_i         => txqt_dat,
+
+                       parity_en_in  => parity_en,
+                       parity_odd_in => parity_odd,
+                       stop_bits_in  => stop_bits,
+                       divisor_in    => divisor_reg,
+
+                       tx_out        => tx_internal
+               );
+
+       -- Transmit FIFO
+       e_tx_fifo: entity utility.fifo
+               generic map (
+                       WIDTH    => 8,
+                       COUNT    => 16
+               )
+               port map (
+                       rst_i    => rst_tx,
+                       clk_i    => clk_i,
+
+                       h_stb_i  => txqh_stb,
+                       h_ack_o  => txqh_ack,
+                       h_dat_i  => dat_i,
+
+                       t_stb_o  => txqt_stb,
+                       t_ack_i  => txqt_ack,
+                       t_dat_o  => txqt_dat,
+
+                       is_empty => txq_empty,
+                       is_full  => txq_full
+               );
+
+       -- Receiver
+       e_rx: entity work.rs232_rx
+               port map (
+                       rst_i      => rst_rx,
+                       clk_i      => clk_i,
+
+                       stb_o      => rxqh_stb,
+                       ack_i      => rxqh_ack,
+                       dat_o      => rxqh_dat,
+
+                       divisor_in => divisor_reg,
+                       parity_in  => parity_en,
+
+                       missed_out => err_missed_int,
+                       parity_out => err_parity_int,
+
+                       rx_in      => rx_internal
+               );
+
+       -- Receive FIFO
+       e_rx_fifo: entity utility.fifo
+               generic map (
+                       WIDTH    => 8,
+                       COUNT    => 16
+               )
+               port map (
+                       rst_i    => rst_rx,
+                       clk_i    => clk_i,
+
+                       h_stb_i  => rxqh_stb,
+                       h_ack_o  => rxqh_ack,
+                       h_dat_i  => rxqh_dat,
+
+                       t_stb_o  => rxqt_stb,
+                       t_ack_i  => rxqt_ack,
+                       t_dat_o  => rxqt_dat,
+
+                       is_empty => rxq_empty,
+                       is_full  => rxq_full
+               );
+
+end behavioral;
diff --git a/libraries/rs232/tests/program.sh b/libraries/rs232/tests/program.sh
new file mode 100755 (executable)
index 0000000..e2092f0
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f test_nexys2.bit prog
+
diff --git a/libraries/rs232/tests/test_nexys2.vhd b/libraries/rs232/tests/test_nexys2.vhd
new file mode 100644 (file)
index 0000000..1c16dba
--- /dev/null
@@ -0,0 +1,85 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library nexys2_lib;
+library work;
+
+
+entity test_nexys2 is
+       port (
+               clk_50: in  std_logic;
+               seg:    out std_logic_vector(6 downto 0);
+               dp:     out std_logic;
+               an:     out std_logic_vector(3 downto 0);
+               Led:    out std_logic_vector(7 downto 0);
+               sw:     in  std_logic_vector(7 downto 0);
+               btn:    in  std_logic_vector(3 downto 0);
+               RsRx:   in  std_logic;
+               RsTx:   out std_logic
+       );
+end test_nexys2;
+
+
+architecture behavioral of test_nexys2 is
+
+       signal rst:      std_logic;
+       signal clk:      std_logic;
+
+       signal cyc:      std_logic;
+       signal stb:      std_logic;
+       signal we:       std_logic;
+       signal ack:      std_logic;
+       signal adr:      std_logic_vector(7 downto 0);
+       signal dat_mosi: std_logic_vector(7 downto 0);
+       signal dat_miso: std_logic_vector(7 downto 0);
+
+begin
+
+       e_uut: entity work.rs232_uart
+               port map (
+                       rst_i        => rst,
+                       clk_i        => clk,
+
+                       cyc_i        => cyc,
+                       stb_i        => stb,
+                       we_i         => we,
+                       ack_o        => ack,
+                       adr_i        => adr(2 downto 0),
+                       dat_i        => dat_mosi,
+                       dat_o        => dat_miso,
+
+                       rx_ready     => Led(0),
+                       rx_full      => Led(1),
+                       tx_ready     => Led(2),
+                       tx_empty     => Led(3),
+                       err_break    => Led(4),
+                       err_framing  => Led(5),
+                       err_parity   => Led(6),
+                       err_overflow => Led(7),
+
+                       tx           => RsTx,
+                       rx           => RsRx
+               );
+
+       e_wb: entity nexys2_lib.wishbone_debugger
+               port map (
+                       clk_o  => clk,
+                       rst_o  => rst,
+
+                       cyc_o  => cyc,
+                       stb_o  => stb,
+                       we_o   => we,
+                       ack_i  => ack,
+                       adr_o  => adr,
+                       dat_o  => dat_mosi,
+                       dat_i  => dat_miso,
+
+                       clk_50 => clk_50,
+                       seg    => seg,
+                       dp     => dp,
+                       an     => an,
+                       sw     => sw,
+                       btn    => btn
+               );
+
+end behavioral;
diff --git a/libraries/rs232/tests/test_rs232_rx.vhd b/libraries/rs232/tests/test_rs232_rx.vhd
new file mode 100644 (file)
index 0000000..cc61842
--- /dev/null
@@ -0,0 +1,240 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_arith.all;
+use ieee.std_logic_unsigned.all;
+use ieee.std_logic_misc.all;
+
+library work;
+
+
+entity test_rs232_rx is
+end test_rs232_rx;
+
+
+architecture behavioral of test_rs232_rx is
+
+       type tx_state_t is (
+               S_TX_INIT,
+               S_TX_A5, S_TX_A5_DONE,
+               S_TX_C2, S_TX_C2_DONE,
+               S_TX_FF, S_TX_FF_DONE,
+               S_TX_00, S_TX_00_DONE,
+               S_TX_DONE
+       );
+
+       signal tx_state: tx_state_t;
+
+       signal clk_50M: std_logic;
+       signal rst: std_logic;
+
+       signal uut_rx: std_logic;
+
+       signal ready: std_logic;
+       signal missed: std_logic;
+       signal ack: std_logic;
+       signal parity: std_logic;
+       signal data: std_logic_vector(7 downto 0);
+
+begin
+
+       -- 50MHz clock
+       p_clock_50M: process
+       begin
+               clk_50M <= '0';
+               wait for 10 ns;
+               clk_50M <= '1';
+               wait for 10 ns;
+       end process;
+
+
+       -- UART Rx block to test
+       uut: entity work.rs232_rx
+               port map (
+                       rst_i      => rst,
+                       clk_i      => clk_50M,
+
+                       stb_o      => ready,
+                       ack_i      => ack,
+                       dat_o      => data,
+
+                       divisor_in => x"1458",
+                       parity_in  => '0',
+
+                       missed_out => missed,
+                       parity_out => parity,
+
+                       rx_in      => uut_rx
+               );
+
+
+       -- Test receive
+       test_rx: process
+       begin
+               ack <= '0';
+
+               rst <= '1';
+               wait until rising_edge(clk_50M);
+               wait until rising_edge(clk_50M);
+               rst <= '0';
+
+               wait until ready = '1';
+               assert data = x"a5" report "Failed to receive A5";
+               assert missed = '0' report "Spurrious miss reported";
+               ack <= '1';
+               wait for 30 ns;
+               ack <= '0';
+               assert ready = '0';
+               assert missed = '0';
+               assert parity = '0';
+
+               wait until ready = '1';
+               assert data = x"c2";
+               assert missed = '0';
+               assert parity = '1';
+
+               wait until tx_state = S_TX_FF_DONE;
+               assert data = x"ff";
+               assert ready = '1';
+               assert missed = '1';
+               ack <= '1';
+               wait for 30 ns;
+               ack <= '0';
+
+               wait until ready = '1';
+               assert data = x"00";
+               assert missed = '0';
+               ack <= '1';
+               wait for 30 ns;
+               ack <= '0';
+
+               wait;
+       end process;
+
+
+       -- RS232 data being sent to the UUT
+       p_data_in: process
+               type parity_t is (P_NONE, P_EVEN, P_ODD);
+
+               -- Parameters
+               constant baud_rate:  integer  := 9600;
+               constant start_bits: real     := 1.0;
+               constant data_bits:  integer  := 8;
+               constant parity:     parity_t := P_NONE;
+               constant stop_bits:  real     := 1.0;
+
+               -- Derived constants
+               constant bit_time: time := (1000 ms / baud_rate);
+
+               variable tx_value: std_logic_vector(data_bits-1 downto 0);
+       begin
+               -- Setup
+               tx_state <= S_TX_INIT;
+               uut_rx <= '1';
+               wait for bit_time * 2;
+
+               -- Transmit A5 
+               tx_state <= S_TX_A5;
+               tx_value := x"a5";
+
+               uut_rx <= '0';
+               wait for bit_time * start_bits;
+               for i in 0 to data_bits-1 loop
+                       uut_rx <= tx_value(i);
+                       wait for bit_time;
+               end loop;
+               case parity is
+                       when P_EVEN =>
+                               uut_rx <= xor_reduce(tx_value);
+                               wait for bit_time;
+                       when P_ODD =>
+                               uut_rx <= xor_reduce(tx_value) xor '1';
+                               wait for bit_time;
+                       when others =>
+                               null;
+               end case;
+               uut_rx <= '1';
+               wait for bit_time * stop_bits;
+               tx_state <= S_TX_A5_DONE;
+               wait for 20 ns;
+
+               -- Transmit C3
+               tx_state <= S_TX_C2;
+               tx_value := x"c2";
+
+               uut_rx <= '0';
+               wait for bit_time * start_bits;
+               for i in 0 to data_bits-1 loop
+                       uut_rx <= tx_value(i);
+                       wait for bit_time;
+               end loop;
+               case parity is
+                       when P_EVEN =>
+                               uut_rx <= xor_reduce(tx_value);
+                               wait for bit_time;
+                       when P_ODD =>
+                               uut_rx <= xor_reduce(tx_value) xor '1';
+                               wait for bit_time;
+                       when others =>
+                               null;
+               end case;
+               uut_rx <= '1';
+               wait for bit_time * stop_bits;
+               tx_state <= S_TX_C2_DONE;
+               wait for 20 ns;
+
+               -- Transmit FF
+               tx_state <= S_TX_FF;
+               tx_value := x"ff";
+
+               uut_rx <= '0';
+               wait for bit_time * start_bits;
+               for i in 0 to data_bits-1 loop
+                       uut_rx <= tx_value(i);
+                       wait for bit_time;
+               end loop;
+               case parity is
+                       when P_EVEN =>
+                               uut_rx <= xor_reduce(tx_value);
+                               wait for bit_time;
+                       when P_ODD =>
+                               uut_rx <= xor_reduce(tx_value) xor '1';
+                               wait for bit_time;
+                       when others =>
+                               null;
+               end case;
+               uut_rx <= '1';
+               wait for bit_time * stop_bits;
+               tx_state <= S_TX_FF_DONE;
+               wait for 20 ns;
+
+               -- Transmit 00
+               tx_state <= S_TX_00;
+               tx_value := x"00";
+
+               uut_rx <= '0';
+               wait for bit_time * start_bits;
+               for i in 0 to data_bits-1 loop
+                       uut_rx <= tx_value(i);
+                       wait for bit_time;
+               end loop;
+               case parity is
+                       when P_EVEN =>
+                               uut_rx <= xor_reduce(tx_value);
+                               wait for bit_time;
+                       when P_ODD =>
+                               uut_rx <= xor_reduce(tx_value) xor '1';
+                               wait for bit_time;
+                       when others =>
+                               null;
+               end case;
+               uut_rx <= '1';
+               wait for bit_time * stop_bits;
+               tx_state <= S_TX_00_DONE;
+               wait for 20 ns;
+
+               -- Done
+               tx_state <= S_TX_DONE;
+               wait;
+       end process p_data_in;
+
+end behavioral;
diff --git a/libraries/rs232/tests/test_rs232_tx.vhd b/libraries/rs232/tests/test_rs232_tx.vhd
new file mode 100644 (file)
index 0000000..a36c297
--- /dev/null
@@ -0,0 +1,100 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_misc.all;
+
+library work;
+
+
+entity test_rs232_tx is
+end test_rs232_tx;
+
+
+architecture behavioral of test_rs232_tx is
+
+       type tx_state_t is (
+               S_TX_INIT,
+               S_TX_A5, S_TX_A5_DONE,
+               S_TX_C3, S_TX_C3_DONE,
+               S_TX_FF, S_TX_FF_DONE,
+               S_TX_00, S_TX_00_DONE,
+               S_TX_DONE
+       );
+       
+       type test_state_t is (
+               S_TEST_RESET,
+               S_TEST_DONE
+       );
+
+       signal tx_state: tx_state_t;
+       signal test_state: test_state_t;
+
+       signal clk_50M: std_logic;
+       signal rst: std_logic;
+
+       signal uut_tx: std_logic;
+
+       signal ready: std_logic;
+       signal start: std_logic;
+       signal data: std_logic_vector(7 downto 0);
+
+begin
+
+       -- 50MHz clock
+       p_clock_50M: process
+       begin
+               clk_50M <= '0';
+               wait for 10 ns;
+               clk_50M <= '1';
+               wait for 10 ns;
+       end process p_clock_50M;
+
+
+       -- UART Tx block to test
+       uut: entity work.rs232_tx
+               port map (
+                       rst_i         => rst,
+                       clk_i         => clk_50M,
+
+                       stb_i         => start,
+                       ack_o         => ready,
+                       dat_i         => data,
+
+                       parity_en_in  => '0',
+                       parity_odd_in => '0',
+                       stop_bits_in  => '0',
+                       divisor_in    => x"1458",
+
+                       tx_out        => uut_tx
+               );
+
+
+       -- Test transmit
+       test_tx: process
+       begin
+               start <= '0';
+               data <= x"00";
+
+               rst <= '1';
+               wait until rising_edge(clk_50M);
+               wait until rising_edge(clk_50M);
+               rst <= '0';
+
+               assert ready = '1';
+               data <= x"a5";
+               start <= '1';
+               wait for 30 ns;
+               start <= '0';
+               assert ready = '0';
+
+               wait until ready = '1';
+               data <= x"c3";
+               start <= '1';
+               wait for 30 ns;
+               start <= '0';
+               assert ready = '0';
+
+               wait;
+       end process;
+
+end behavioral;
diff --git a/libraries/rs232/tests/test_uart.vhd b/libraries/rs232/tests/test_uart.vhd
new file mode 100644 (file)
index 0000000..27f1145
--- /dev/null
@@ -0,0 +1,180 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+entity test_uart is
+end test_uart;
+
+architecture behavior of test_uart is
+
+       constant clk_i_period: time := 20 ns;
+
+       signal rst_i:        std_logic;
+       signal clk_i:        std_logic;
+
+       signal cyc_i:        std_logic;
+       signal stb_i:        std_logic;
+       signal we_i:         std_logic;
+       signal ack_o:        std_logic;
+       signal adr_i:        std_logic_vector(2 downto 0);
+       signal dat_i:        std_logic_vector(7 downto 0);
+       signal dat_o:        std_logic_vector(7 downto 0);
+
+       signal rx_ready:     std_logic;
+       signal rx_full:      std_logic;
+       signal tx_ready:     std_logic;
+       signal tx_empty:     std_logic;
+       signal break_err:    std_logic;
+       signal framing_err:  std_logic;
+       signal parity_err:   std_logic;
+       signal overflow_err: std_logic;
+
+       signal rx:           std_logic;
+       signal tx:           std_logic;
+
+begin
+
+       test: process
+       begin
+               -- Initial signal values
+               --rx    <= '0';
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               adr_i <= "000";
+               dat_i <= x"00";
+
+               -- Reset
+               rst_i <= '1';
+               wait for clk_i_period*2;
+               rst_i <= '0';
+
+               -- Configure
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '1';
+               adr_i <= "001";
+               dat_i <= x"58";
+               if ack_o = '0' then wait until ack_o = '1'; end if;
+               wait until rising_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               adr_i <= "000";
+               dat_i <= x"00";
+
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '1';
+               adr_i <= "010";
+               dat_i <= x"14";
+               if ack_o = '0' then wait until ack_o = '1'; end if;
+               wait until rising_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               adr_i <= "000";
+               dat_i <= x"00";
+
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '1';
+               adr_i <= "000";
+               dat_i <= "00000011";
+               if ack_o = '0' then wait until ack_o = '1'; end if;
+               wait until rising_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               adr_i <= "000";
+               dat_i <= x"00";
+
+               -- Transmit byte
+               cyc_i <= '1';
+               stb_i <= '1';
+               we_i  <= '1';
+               adr_i <= "101";
+               dat_i <= x"A5";
+               if ack_o = '0' then wait until ack_o = '1'; end if;
+               wait until rising_edge(clk_i);
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               adr_i <= "000";
+               dat_i <= x"00";
+
+               wait;
+       end process;
+
+       rs232: process
+       begin
+               rx <= '1';
+               wait for 500 us;
+
+               -- 1 start bit
+               rx <= '0';
+               wait for 100 us;
+
+               -- 8 data bits
+               rx <= '1';
+               wait for 100 us;
+               rx <= '1';
+               wait for 100 us;
+               rx <= '0';
+               wait for 100 us;
+               rx <= '0';
+               wait for 100 us;
+               rx <= '0';
+               wait for 100 us;
+               rx <= '1';
+               wait for 100 us;
+               rx <= '0';
+               wait for 100 us;
+               rx <= '1';
+               wait for 100 us;
+
+               -- 0 parity bits
+
+               -- 2 stop bits
+               rx <= '1';
+               wait for 200 us;
+
+               wait;
+       end process;
+
+       uut: entity work.rs232_uart
+               port map (
+                       rst_i        => rst_i,
+                       clk_i        => clk_i,
+
+                       cyc_i        => cyc_i,
+                       stb_i        => stb_i,
+                       we_i         => we_i,
+                       ack_o        => ack_o,
+                       adr_i        => adr_i,
+                       dat_i        => dat_i,
+                       dat_o        => dat_o,
+
+                       rx_ready     => rx_ready,
+                       rx_full      => rx_full,
+                       tx_ready     => tx_ready,
+                       tx_empty     => tx_empty,
+                       break_err    => break_err,
+                       framing_err  => framing_err,
+                       parity_err   => parity_err,
+                       overflow_err => overflow_err,
+
+                       tx           => tx,
+                       rx           => rx
+               );
+
+       clk_i_process: process
+       begin
+               clk_i <= '0';
+               wait for clk_i_period/2;
+               clk_i <= '1';
+               wait for clk_i_period/2;
+       end process;
+
+end;
diff --git a/libraries/simulated/delay_edges.vhd b/libraries/simulated/delay_edges.vhd
new file mode 100644 (file)
index 0000000..42ffd41
--- /dev/null
@@ -0,0 +1,28 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity delay_edges is
+       generic (
+               D_RISE: time := 0 ns;
+               D_FALL: time := 0 ns
+       );
+       port (
+               sig_in:  in  std_logic;
+               sig_out: out std_logic
+       );
+end delay_edges;
+
+architecture behavioral of delay_edges is
+begin
+
+       delayed_a: if D_RISE > D_FALL generate
+               sig_out <= sig_in'delayed(D_RISE) and sig_in'delayed(D_FALL);
+       end generate;
+
+       delayed_b: if D_RISE <= D_FALL generate
+               sig_out <= sig_in'delayed(D_RISE) or sig_in'delayed(D_FALL);
+       end generate;
+
+end behavioral;
+
diff --git a/libraries/simulated/is_unstable.vhd b/libraries/simulated/is_unstable.vhd
new file mode 100644 (file)
index 0000000..b0e0b58
--- /dev/null
@@ -0,0 +1,24 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity is_unstable is
+       generic (
+               T:      time := 0 ns;
+               T_HOLD: time := 0 ns
+       );
+       port (
+               sig_in:  in  std_logic;
+               sig_out: out std_logic
+       );
+end is_unstable;
+
+
+architecture behavioral of is_unstable is
+begin
+
+       sig_out <= '1' when (sig_in'delayed(T_HOLD) /= sig_in'delayed(T)) or
+                           (sig_in'last_event < T and sig_in'last_event >= T_HOLD)
+                      else '0';
+
+end behavioral;
diff --git a/libraries/simulated/is_unstable_v.vhd b/libraries/simulated/is_unstable_v.vhd
new file mode 100644 (file)
index 0000000..3b447da
--- /dev/null
@@ -0,0 +1,24 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity is_unstable_v is
+       generic (
+               T:      time := 0 ns;
+               T_HOLD: time := 0 ns
+       );
+       port (
+               sig_in:  in  std_logic_vector;
+               sig_out: out std_logic
+       );
+end is_unstable_v;
+
+
+architecture behavioral of is_unstable_v is
+begin
+
+       sig_out <= '1' when (sig_in'delayed(T_HOLD) /= sig_in'delayed(T)) or
+                           (sig_in'last_event < T and sig_in'last_event >= T_HOLD)
+                      else '0';
+
+end behavioral;
diff --git a/libraries/simulated/js28f128j3d75.vhd b/libraries/simulated/js28f128j3d75.vhd
new file mode 100644 (file)
index 0000000..4068dbf
--- /dev/null
@@ -0,0 +1,1143 @@
+--------------------------------------------------------------------------------
+-- Notes:
+--
+-- In ISim 14.7, it appears that the 'stable attribute doesn't work
+-- Checking if 'last_event >= T_x doesn't seem to work with no previous events
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity js28f128j3d75 is
+       port (
+               a:      in    std_logic_vector(23 downto 0);
+               d:      inout std_logic_vector(15 downto 0);
+               ce:     in    std_logic_vector(2 downto 0);
+               rp_n:   in    std_logic;
+               oe_n:   in    std_logic;
+               we_n:   in    std_logic;
+               sts:    out   std_logic;  -- open drain
+               byte_n: in    std_logic;
+               vpen:   in    std_logic
+       );
+end js28f128j3d75;
+
+
+architecture behavioral of js28f128j3d75 is
+
+       -- Timings for 128Mbit, J3D-75 parts
+
+       -- Read timings
+       constant T_AVAV: time := 75 ns;    -- R1, Read/Write Cycle Time
+       constant T_AVQV: time := 75 ns;    -- R2, Address to Output Delay
+       constant T_ELQV: time := 75 ns;    -- R3, CEx to Output Delay
+       constant T_GLQV_NA: time := 25 ns; -- R4, OE# to Non-Array Output Delay
+       constant T_PHQV: time := 210 ns;   -- R5, RP# High to Output Delay
+       constant T_ELQX: time := 0 ns;     -- R6, CEx to Output Low Z
+       constant T_GLQX: time := 0 ns;     -- R7, OE# to Output Low Z
+       constant T_EHQZ: time := 25 ns;    -- R8, CEx High to Output in High Z
+       constant T_GHQZ: time := 15 ns;    -- R9, OE# High to Output in High Z
+       constant T_OH:   time := 0 ns;     -- R10, Output Hold from Address, CEx, or OE# change
+       constant T_ELFL: time := 10 ns;    -- R11, CEx Low to BYTE# High or Low
+       constant T_ELFH: time := 10 ns;    -- R11, (Same as Above)
+       constant T_FLQV: time := 1 ns;     -- R12, BYTE# to Output Delay
+       constant T_FHQV: time := 1 ns;     -- R12, (Same as Above)
+       constant T_FLQZ: time := 1 ns;     -- R13, BYTE# to Output in High Z
+       constant T_EHEL: time := 0 ns;     -- R14, CEx High to CEx Low
+       constant T_APA:  time := 25 ns;    -- R15, Page Address Access Time
+       constant T_GLQV_A: time := 25 ns;  -- R16, OE# to Array Output Delay
+
+       -- Write timings
+       constant T_PHWL: time := 210 ns;   -- W1, RP# High Recovery to WE# (CEx) Going Low
+       constant T_PHEL: time := 210 ns;   -- W1, (Same as Above)
+       constant T_ELWL: time := 0 ns;     -- W2, CEx (WE#) Low to WE# (CEx) Going Low
+       constant T_WLEL: time := 0 ns;     -- W2, (Same as Above)
+       constant T_WP:   time := 60 ns;    -- W3, Write Pulse Width
+       constant T_DVWH: time := 50 ns;    -- W4, Data Setup to WE# (CEx) Going High
+       constant T_DVEH: time := 50 ns;    -- W4, (Same as Above)
+       constant T_AVWH: time := 55 ns;    -- W5, Address Setup to WE# (CEx) Going High
+       constant T_AVEH: time := 55 ns;    -- W5, (Same as Above)
+       constant T_WHEH: time := 0 ns;     -- W6, CEx (WE#) Hold from WE# (CEx) High
+       constant T_EHWH: time := 0 ns;     -- W6, (Same as Above)
+       constant T_WHDX: time := 0 ns;     -- W7, Data Hold from WE# (CEx) High
+       constant T_EHDX: time := 0 ns;     -- W7, (Same as Above)
+       constant T_WHAX: time := 0 ns;     -- W8, Address Hold from WE# (CEx) High
+       constant T_EHAX: time := 0 ns;     -- W8, (Same as Above)
+       constant T_WPH:  time := 30 ns;    -- W9, Write Pulse Width High
+       constant T_VPWH: time := 0 ns;     -- W11, Vpen Setup to WE# (CEx) Going High
+       constant T_VPEH: time := 0 ns;     -- W11, (Same as Above)
+       constant T_WHGL: time := 35 ns;    -- W12, Write Recovery before Read
+       constant T_EHGL: time := 35 ns;    -- W12, (Same as Above)
+       constant T_WHRL: time := 500 ns;   -- W13, WE# (CEx) High to STS Going Low
+       constant T_EHRL: time := 500 ns;   -- W13, (Same as Above)
+       constant T_QVVL: time := 0 ns;     -- W15, Vpen Hold from Valid SRD, STS Going High
+
+       -- Configuration timings
+       constant T_WHQV3: time := 175 us;  -- W16, Byte Program Time (Using Word/Byte Program Command)
+       constant T_EHQV3: time := 175 us;  -- W16, (Same as above)
+       constant T_WHQV4: time := 4 sec;   -- W16, Block Erase Time
+       constant T_EHQV4: time := 4 sec;   -- W16, (Same as above)
+       constant T_WHQV5: time := 60 us;   -- W16, Set Lock-Bit Time
+       constant T_EHQV5: time := 60 us;   -- W16, (Same as above)
+       constant T_WHQV6: time := 0.7 sec; -- W16, Clear Block Lock-Bits Time
+       constant T_EHQV6: time := 0.7 sec; -- W16, (Same as above)
+       constant T_WHRH1: time := 20 us;   -- W16, Program Suspend Latency Time to Read
+       constant T_EHRH1: time := 20 us;   -- W16, (Same as above)
+       constant T_WHRH:  time := 20 us;   -- W16, Erase Suspend Latency Time to Read
+       constant T_EHRH:  time := 20 us;   -- W16, (Same as above)
+       constant T_STS:   time := 500 ns;  -- WY, STS Pulse Width Low Time
+
+       -- Reset timings
+       constant T_PLPH: time := 25 us;    -- P1, RP# Pulse Low Time
+       constant T_PHRH: time := 100 ns;   -- P2, RP# High to Reset During Block-Erase, Program, or Lock-Bit Configuration
+       constant T_VCCPH: time := 60 us;   -- P3, Vcc Power Valid to RP# de-assertion (high)
+
+       pure function block_index_of(address: std_logic_vector(a'high downto 0)) return integer is
+       begin
+               return to_integer(unsigned(address(address'high downto 17)));
+       end function;
+
+       pure function word_index_of(address: std_logic_vector(a'high downto 0)) return integer is
+       begin
+               return to_integer(unsigned(address(16 downto 1)));
+       end function;
+
+       -- Internal types
+       type word_array is array(natural range <>) of std_logic_vector(15 downto 0);
+       type block_array is array(natural range<>) of word_array(65535 downto 0);
+
+       type cui_state is (
+               C_IDLE,
+               C_PECR,
+               C_POTP,
+               C_STSC,
+               C_PROG,
+               C_BUF_PROG_COUNT,
+               C_BUF_PROG_DATA,
+               C_BUF_PROG_CONF,
+               C_ERASE,
+               C_LOCK
+       );
+       type read_state is (R_ARRAY, R_ID, R_STATUS, R_CFI);
+       type wsm_state is (W_READY, W_PROG, W_BUF_PROG, W_ERASE, W_LOCK_PROG, W_LOCK_ERASE, W_OTP_PROG, W_RESUME);
+
+       -- Internal signals
+       signal internal_ce_n: std_logic;  -- Composite chip-enable from multiple CE input signals
+       signal internal_we_n: std_logic;  -- Composite write-latch from WE# and CEx
+
+       signal array_pre_word: std_logic_vector(15 downto 0);
+       signal array_word: std_logic_vector(15 downto 0);
+       signal cfi_pre_word: std_logic_vector(15 downto 0);
+       signal cfi_word: std_logic_vector(15 downto 0);
+       signal id_pre_word: std_logic_vector(15 downto 0);
+       signal id_word: std_logic_vector(15 downto 0);
+       signal status_pre_word: std_logic_vector(15 downto 0);
+       signal status_word: std_logic_vector(15 downto 0);
+       signal read_pre_word: std_logic_vector(15 downto 0);
+       signal read_word: std_logic_vector(15 downto 0);
+       signal last_word: std_logic_vector(15 downto 0);
+
+       -- Delay mask signals for array reads
+       signal addr_hi_change_4: std_logic;
+       signal addr_hi_change_8: std_logic;
+       signal addr_hi_change: std_logic;
+       signal is_sensing: std_logic;
+       signal addr_lo_change_4: std_logic;
+       signal addr_lo_change_8: std_logic;
+       signal addr_lo_change: std_logic;
+       signal oe_change_array: std_logic;
+
+       -- Delay mask signals for non-array reads
+       signal addr_change: std_logic;
+       signal oe_change_nonarray: std_logic;
+
+       -- Delay mask signals for all reads
+       signal rp_change: std_logic;
+       signal ce_change: std_logic;
+       signal byte_change: std_logic;
+
+       -- Delay mask signals for tristate logic
+       signal ce_n_gate: std_logic;
+       signal oe_n_gate: std_logic;
+       signal final_oe_n: std_logic;
+
+       signal cur_cui_state: cui_state;
+       signal cur_read_state: read_state;
+       signal cur_wsm_state: wsm_state;
+       signal next_wsm_state: wsm_state;
+       signal wsm_trigger: std_logic;
+
+       -- Status signals
+       signal sts_ready: std_logic := '1';
+       signal sts_susp_erase: std_logic := '0';
+       signal sts_error_code: std_logic_vector(1 downto 0) := "00";
+       signal sts_error_volt: std_logic := '0';
+       signal sts_susp_prog: std_logic := '0';
+       signal sts_error_lock: std_logic := '0';
+       signal sts_config: std_logic_vector(7 downto 0) := (others => '0');
+
+       -- Flash array data
+       -- Only two blocks to save the simulator
+       signal flash_array: block_array(1 downto 0) := (others => (others => (others => '1')));
+
+       -- Lock operation state
+       signal lock_address: std_logic_vector(a'range);
+       signal lock_reg: std_logic_vector(127 downto 0) := (others => '0');
+
+       -- Program array operation state
+       signal prog_count: integer;
+       signal prog_address: std_logic_vector(a'range);
+       signal prog_buf: word_array(31 downto 0);
+       signal prog_word: std_logic_vector(15 downto 0);
+
+       -- Erase operation state
+       signal erase_address: std_logic_vector(a'range);
+
+       -- Enhanced Configuration Register
+       signal ecr_reg: std_logic_vector(16 downto 0);
+       signal ecr_8word_page: std_logic;
+
+       -- OTP Protection Register
+       signal otp_prog_offset: std_logic_vector(a'range);
+       signal otp_prog_data: std_logic_vector(15 downto 0);
+       signal plr_reg: std_logic_vector(15 downto 0) := x"FFFE";
+       signal otp_reg: word_array(7 downto 0) := (others => (others => '1'));
+
+       -- Debug
+       signal ce_stable: boolean;
+begin
+
+       ----------------------------------------------------------------------------
+       -- Useful internal signals composed from external signals
+
+       -- Decode multiple chip enable inputs into single chip enable signal
+       with ce select internal_ce_n <=
+               '0' when "000",
+               '1' when "001",
+               '1' when "010",
+               '1' when "011",
+               '0' when "100",
+               '0' when "101",
+               '0' when "110",
+               '1' when "111",
+               'X' when others;
+
+       -- Write latch is triggered by either WEx or CEx going high, whichever is first
+       internal_we_n <= we_n or internal_ce_n;
+
+       -- STS signal represents the state of the WSM
+       sts <= 'H' when sts_ready = '1' else '0';
+
+
+       ----------------------------------------------------------------------------
+       -- Reset logic
+
+       reset: process (rp_n)
+       begin
+               if rp_n = '0' then
+                       -- reset all state machines
+                       -- cur_cui_state <= C_IDLE;
+                       -- cur_read_state <= R_ARRAY;
+                       -- cur_wsm_state <= W_READY;
+
+                       -- reset status register and status config
+                       --sts_ready <= '1';
+                       --sts_susp_erase <= '0';
+                       --sts_error_code <= "00";
+                       --sts_error_volt <= '0';
+                       --sts_susp_prog <= '0';
+                       --sts_error_lock <= '0';
+                       sts_config <= (others => '0');
+
+                       -- reset ECR to 4-word page mode
+                       ecr_reg <= (others => '0');
+
+                       -- abort all program, erase, lock-bit operations
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Read logic
+
+       -- Look up value from flash array
+       array_pre_word <= flash_array(block_index_of(a))(word_index_of(a));
+
+       -- Generate mask signal representing sensing delay
+       -- ce_change is incorporated here because the datasheet says ce falling triggers sensing,
+       -- but the same delay also listed for all other reads, so it is incorporated again below.
+       addr_hi_change_4 <= '1' when (a(a'high downto 3) /= a(a'high downto 3)'delayed(T_AVQV)) or
+                                    (a(a'high downto 3)'last_event < T_AVQV)
+                               else '0';
+       addr_hi_change_8 <= '1' when (a(a'high downto 4) /= a(a'high downto 4)'delayed(T_AVQV)) or
+                                    (a(a'high downto 4)'last_event < T_AVQV)
+                               else '0';
+       with ecr_8word_page select addr_hi_change <=
+               addr_hi_change_8 when '1',
+               addr_hi_change_4 when others;
+       ce_change <= '1' when (internal_ce_n /= internal_ce_n'delayed(T_ELQV)) or
+                             (internal_ce_n'last_event < T_ELQV)
+                        else '0';
+       is_sensing <= addr_hi_change or ce_change;
+
+       -- Generate mask signal representing word select delay
+       addr_lo_change_4 <= '1' when (a(2 downto 0) /= a(2 downto 0)'delayed(T_APA)) or
+                                    (a(2 downto 0)'last_event < T_APA)
+                               else '0';
+       addr_lo_change_8 <= '1' when (a(3 downto 0) /= a(3 downto 0)'delayed(T_APA)) or
+                                    (a(3 downto 0)'last_event < T_APA)
+                               else '0';
+       with ecr_8word_page select addr_lo_change <=
+               addr_lo_change_8 when '1',
+               addr_lo_change_4 when others;
+
+       -- Generate mask signal representing OE# delay for array reads
+       oe_change_array <= '1' when (oe_n /= oe_n'delayed(T_GLQV_A)) or
+                                   (oe_n'last_event < T_GLQV_A)
+                              else '0';
+
+       array_word <= (others => 'X') when is_sensing = '1' or
+                                          addr_lo_change = '1' or
+                                                                          oe_change_array = '1' else array_pre_word;
+
+
+       -- Generate mask signals representing delays for non-array reads (and use ce_change from above)
+       -- Not sure if T_AVAV applies to non-array reads, so apply it anyway just in case
+       addr_change <= '1' when (a /= a'delayed(T_AVQV)) or
+                               (a'last_event < T_AVQV)
+                          else '0';
+       oe_change_nonarray <= '1' when (oe_n /= oe_n'delayed(T_GLQV_NA)) or
+                                      (oe_n'last_event < T_GLQV_NA)
+                                 else '0';
+
+       -- Look up CFI query value
+       do_cfi_read: process (a)
+       begin
+               case a(8 downto 1) is
+                       when x"10" => cfi_pre_word <= x"0051";
+                       when x"11" => cfi_pre_word <= x"0052";
+                       when x"12" => cfi_pre_word <= x"0059";
+                       when others => cfi_pre_word <= (others => '0');
+               end case;
+       end process;
+
+       cfi_word <= (others => 'X') when addr_change = '1' or
+                                       oe_change_nonarray = '1' else cfi_pre_word;
+
+       -- Look up ID register value
+       do_id_read: process (a)
+       begin
+               case to_integer(unsigned(a(16 downto 1))) is
+                       -- Device ID
+                       when 16#0001# =>
+                               id_pre_word <= x"0018";
+
+                       -- Lock bit for this block
+                       when 16#0002# =>
+                               id_pre_word <= (others => '0');
+                               id_pre_word(0) <= lock_reg(to_integer(unsigned(a(a'high downto 17))));
+
+                       -- OTP protection lock register
+                       when 16#0080# => id_pre_word <= plr_reg;
+
+                       -- OTP register
+                       when 16#0081# to 16#88# =>
+                               id_pre_word <= otp_reg(to_integer(unsigned(a)) - 16#81#);
+
+                       when others => id_pre_word <= (others => 'X');
+               end case;
+       end process;
+
+       id_word <= (others => 'X') when addr_change = '1' or
+                                       oe_change_nonarray = '1' else id_pre_word;
+
+       -- Compute status register value
+       do_status: process (internal_ce_n, oe_n,
+                           sts_ready, sts_susp_erase, sts_error_code, sts_error_volt, sts_susp_prog, sts_error_lock)
+       begin
+               -- Status register value is latched on CEx or OE# going low
+               if falling_edge(internal_ce_n) or (falling_edge(oe_n) and internal_ce_n = '0') then
+                       status_pre_word(15 downto 8) <= x"00";
+                       status_pre_word(7) <= sts_ready;
+                       status_pre_word(6) <= sts_susp_erase;
+                       status_pre_word(5 downto 4) <= sts_error_code;
+                       status_pre_word(3) <= sts_error_volt;
+                       status_pre_word(2) <= sts_susp_prog;
+                       status_pre_word(1) <= sts_error_lock;
+                       status_pre_word(0) <= '0';
+               end if;
+       end process;
+
+       status_word <= (others => 'X') when addr_change = '1' or
+                                           oe_change_nonarray = '1' else status_pre_word;
+
+
+       -- Choose a value based on read mode
+       do_read: process (cur_read_state, array_word, cfi_word, id_word, status_word)
+       begin
+               case cur_read_state is
+                       when R_ARRAY =>
+                               read_pre_word <= array_word;
+                       when R_ID =>
+                               read_pre_word <= id_word;
+                       when R_STATUS =>
+                               read_pre_word <= status_word;
+                       when R_CFI =>
+                               read_pre_word <= cfi_word;
+                       when others =>
+                               read_pre_word <= (others => 'X');
+               end case;
+       end process;
+
+       -- Generate mask signals for delays that apply to any read
+       rp_change <= '1' when (rp_n /= rp_n'delayed(T_PHQV)) or
+                             (rp_n'last_event < T_PHQV)
+                        else '0';
+       byte_change <= '1' when (byte_n /= byte_n'delayed(T_FLQV)) or
+                               (byte_n'last_event < T_FLQV)
+                          else '0';
+       -- ce_change from above
+
+       -- Handle byte/word modes
+       byte_select: process (read_pre_word, a(0), byte_n)
+       begin
+               if byte_n = '0' then
+                       -- Oops, extra tristating to handle here instead of in tristate logic
+                       -- Good thing is is sim-only!
+                       read_word(15 downto 8) <= (others => 'Z');
+                       if a(0) = '0' then
+                               read_word(7 downto 0) <= read_pre_word(7 downto 0);
+                       else
+                               read_word(7 downto 0) <= read_pre_word(15 downto 8);
+                       end if;
+               else
+                       read_word <= read_pre_word;
+               end if;
+       end process;
+
+       -- Equal parts gin, green chartreuse, maraschino liqueur, lime; shaken and served up
+       last_word <= (others => 'X') when rp_change = '1' or
+                                         byte_change = '1' else read_word;
+
+
+       -- Handle delays between CEx/OE# and transitions between low/high-Z output
+       -- The front and back porch have different delay specs, so to get the front porch
+       -- with one delay and the back porch with the other we may need to either and or
+       -- or the delayed signals depending on which delay is longer.
+       delayed_ce_n_a: if T_ELQX > T_EHQZ generate
+               ce_n_gate <= internal_ce_n'delayed(T_ELQX) or internal_ce_n'delayed(T_EHQZ);
+       end generate;
+       delayed_ce_n_b: if T_ELQX <= T_EHQZ generate
+               ce_n_gate <= internal_ce_n'delayed(T_ELQX) and internal_ce_n'delayed(T_EHQZ);
+       end generate;
+       delayed_oe_n_a: if T_GLQX > T_GHQZ generate
+               oe_n_gate <= oe_n'delayed(T_GLQX) or oe_n'delayed(T_GHQZ);
+       end generate;
+       delayed_oe_n_b: if T_GLQX <= T_GHQZ generate
+               oe_n_gate <= oe_n'delayed(T_GLQX) and oe_n'delayed(T_GHQZ);
+       end generate;
+       final_oe_n <= ce_n_gate or oe_n_gate;
+       d <= (others => 'Z') when rp_n = '0' or final_oe_n = '1' else last_word'delayed(T_OH);
+
+
+       ecr_8word_page <= ecr_reg(13);
+
+
+       ----------------------------------------------------------------------------
+       -- Write logic
+
+       -- Latch CUI on rising edge of we_n or ce_n
+       -- CUI logic is responsible for:
+       -- * checking sts_ready before triggering
+       -- * checking sts_susp_prog/erase before triggering if relevant
+       -- * setting sts_susp_prog/erase when suspending
+       -- * setting next_wsm_state and wsm_trigger to begin operations
+       cui: process (internal_we_n, a, d, rp_n,
+                     sts_ready, sts_susp_prog, sts_susp_erase, cur_wsm_state)
+               variable prog_i: integer;
+       begin
+               if rp_n = '0' then
+                       cur_cui_state <= C_IDLE;
+                       cur_read_state <= R_ARRAY;
+                       wsm_trigger <= '0';
+               elsif rising_edge(internal_we_n) then
+                       report "PENIS";
+                       case cur_cui_state is
+                               when C_IDLE =>
+                                       wsm_trigger <= '0';
+
+                                       -- Idle, not waiting for data or confirmation commands
+                                       case d(7 downto 0) is
+                                               -- Program Enhanced Configuration Register (Setup)
+                                               --when x"60" =>
+                                               --      cur_cui_state <= C_PECR;
+
+                                               -- Program OTP Register (Setup)
+                                               when x"c0" =>
+                                                       cur_cui_state <= C_POTP;
+
+                                               -- Clear Status Register
+                                               when x"50" =>
+                                                       -- Only clear error flags, not status
+                                                       sts_error_code <= "00";
+                                                       sts_error_volt <= '0';
+                                                       sts_error_lock <= '0';
+                                                       cur_read_state <= R_STATUS;
+
+                                               -- Program STS Configuration Register (Setup)
+                                               when x"b8" =>
+                                                       -- FIXME: enter R_STATUS?
+                                                       cur_cui_state <= C_STSC;
+
+                                               -- Read Array
+                                               when x"ff" =>
+                                                       assert sts_ready = '1'
+                                                               report "Entering read-array mode while WSM is busy"
+                                                               severity warning;
+                                                       cur_read_state <= R_ARRAY;
+
+                                               -- Read Status Register
+                                               when x"70" =>
+                                                       cur_read_state <= R_STATUS;
+
+                                               -- Read Identifier Codes
+                                               when x"90" =>
+                                                       cur_read_state <= R_ID;
+
+                                               -- CFI Query
+                                               when x"98" =>
+                                                       cur_read_state <= R_CFI;
+
+                                               -- Word/Byte Program (Setup)
+                                               when x"40" | x"10" =>
+                                                       cur_cui_state <= C_PROG;
+
+                                               -- Buffered Program (Setup)
+                                               when x"e8" =>
+                                                       cur_read_state <= R_STATUS;
+                                                       if sts_ready = '1' then
+                                                               cur_cui_state <= C_BUF_PROG_COUNT;
+                                                       end if;
+
+                                               -- BLock Erase (Setup)
+                                               when x"20" =>
+                                                       cur_cui_state <= C_ERASE;
+
+                                               -- Program/Erase Suspend
+                                               when x"b0" =>
+                                                       if sts_ready = '1' then
+                                                               -- No-op
+                                                               null;
+                                                       elsif cur_wsm_state = W_PROG or cur_wsm_state = W_BUF_PROG then
+                                                               -- Suspend program operation
+                                                               sts_susp_prog <= '1';
+                                                       elsif cur_wsm_state = W_ERASE then
+                                                               -- Suspend erase operation
+                                                               sts_susp_erase <= '1';
+                                                       end if;
+                                                       -- Suspend does not change read mode
+
+                                               -- Program/Erase Resume
+                                               when x"d0" =>
+                                                       next_wsm_state <= W_RESUME;
+                                                       wsm_trigger <= '1';
+                                                       cur_read_state <= R_STATUS;
+
+                                               -- Lock/Unlock Block (Setup) and Program Enhanced Configuration Register (Setup)
+                                               when x"60" =>
+                                                       cur_read_state <= R_STATUS;
+                                                       cur_cui_state <= C_LOCK;
+
+                                               when others =>
+                                                       sts_error_code <= "11";
+                                       end case;
+
+                               -- Started Program Enhanced Configuration Register command
+                               when C_PECR =>
+                                       case d(7 downto 0) is
+                                               when x"04" =>
+                                                       ecr_reg <= a(ecr_reg'high downto 0);
+                                                       -- Datasheet says it returns to read-array mode...
+                                                       -- FIXME: Does it just not change the mode?
+                                                       cur_read_state <= R_ARRAY;
+
+                                               when others =>
+                                                       sts_error_code <= "11";
+                                       end case;
+                                       cur_cui_state <= C_IDLE;
+
+                               -- Started Program OTP Register command
+                               when C_POTP =>
+                                       if sts_ready = '0' then
+                                               -- Not allowed while WSM is busy
+                                               sts_error_code <= "11";
+                                       elsif sts_susp_prog = '1' or sts_susp_erase = '1' then
+                                               -- Not allowed while program/erase suspended
+                                               sts_error_code <= "11";
+                                       else
+                                               -- Latch address and data and trigger WSM
+                                               otp_prog_offset <= a;
+                                               otp_prog_data <= d;
+                                               next_wsm_state <= W_OTP_PROG;
+                                               wsm_trigger <= '1';
+                                       end if;
+
+                                       cur_read_state <= R_STATUS;
+                                       cur_cui_state <= C_IDLE;
+
+                               -- Started Program STS Configuration Register command
+                               when C_STSC =>
+                                       if sts_ready = '0' then
+                                               -- Not allowed while WSM is busy
+                                               sts_error_code <= "11";
+                                       elsif sts_susp_prog = '1' or sts_susp_erase = '1' then
+                                               -- Not allowed while program/erase suspended
+                                               sts_error_code <= "11";
+                                       elsif (d and x"00fc") /= x"0000" then
+                                               -- Invalid configuration value
+                                               sts_error_code <= "11";
+                                       else
+                                               assert d(1) = '0' and d(0) = '0'
+                                                       report "TODO: STS pulse config"
+                                                       severity warning;
+                                               sts_config <= d(7 downto 0);
+                                       end if;
+                                       cur_read_state <= R_STATUS;
+                                       cur_cui_state <= C_IDLE;
+
+                               -- Started Word/Byte Program command
+                               when C_PROG =>
+                                       if sts_ready = '0' then
+                                               -- Not allowed while WSM is busy
+                                               sts_error_code <= "11";
+                                       elsif sts_susp_prog = '1' then
+                                               -- Not allowed while program suspended
+                                               sts_error_code <= "11";
+                                       else
+                                               -- Latch address and data and trigger WSM
+                                               prog_address <= a;
+                                               prog_word <= d;
+                                               next_wsm_state <= W_PROG;
+                                               wsm_trigger <= '1';
+                                       end if;
+                                       cur_read_state <= R_STATUS;
+                                       cur_cui_state <= C_IDLE;
+
+                               -- Started Buffer Program command
+                               when C_BUF_PROG_COUNT =>
+                                       if sts_ready = '0' then
+                                               -- sts_ready already checked in previous state, but just in case
+                                               sts_error_code <= "11";
+                                               cur_cui_state <= C_IDLE;
+                                       elsif sts_susp_prog = '1' then
+                                               -- Do not allow while programming operation is suspended
+                                               -- Should this check have been rolled into the status check in
+                                               -- the previous state?  Or should this check be done in the previous
+                                               -- state?  TODO: check on hardware
+                                               sts_error_code <= "11";
+                                               cur_cui_state <= C_IDLE;
+                                       elsif (d(7 downto 0) and x"e0") /= x"0000" then
+                                               -- Word count must be <= 31
+                                               report "Buffer program sent with word count greater than 31" severity warning;
+                                               sts_error_code <= "11";
+                                               cur_cui_state <= C_IDLE;
+                                       else
+                                               -- Latch start address and word count
+                                               prog_address <= a;
+                                               prog_count <= to_integer(unsigned(d));
+                                               prog_i := to_integer(unsigned(d));
+                                               cur_cui_state <= C_BUF_PROG_DATA;
+                                       end if;
+
+                               when C_BUF_PROG_DATA =>
+                                       -- Latch data into buffer at address
+                                       prog_buf(to_integer(unsigned(a(5 downto 1)))) <= d;
+                                       if prog_i = 0 then
+                                               cur_cui_state <= C_BUF_PROG_CONF;
+                                       else
+                                               prog_i := prog_i - 1;
+                                       end if;
+
+                               when C_BUF_PROG_CONF =>
+                                       if d(7 downto 0) /= x"d0" then
+                                               -- Failed to confirm write, abort
+                                               sts_error_code <= "11";
+                                               cur_cui_state <= C_IDLE;
+                                       else
+                                               -- Trigger WSM
+                                               next_wsm_state <= W_BUF_PROG;
+                                               wsm_trigger <= '1';
+                                               cur_cui_state <= C_IDLE;
+                                       end if;
+
+                               -- Started Block Erase command
+                               when C_ERASE =>
+                                       -- TODO: not allowed when erase or program suspended
+                                       if sts_ready = '0' then
+                                               -- Not allowed while WSM is busy
+                                               sts_error_code <= "11";
+                                               cur_cui_state <= C_IDLE;
+                                       elsif sts_susp_prog = '1' or sts_susp_erase = '1' then
+                                               -- Not allowed while program/erase suspended
+                                               sts_error_code <= "11";
+                                               cur_cui_state <= C_IDLE;
+                                       elsif d(7 downto 0) /= x"d0" then
+                                               -- Failed to confirm erase
+                                               sts_error_code <= "11";
+                                               cur_cui_state <= C_IDLE;
+                                       else
+                                               -- Latch block address and trigger WSM
+                                               erase_address <= a;
+                                               next_wsm_state <= W_ERASE;
+                                               wsm_trigger <= '1';
+                                               cur_cui_state <= C_IDLE;
+                                       end if;
+
+                               -- Started Lock/Unlock Block command
+                               when C_LOCK =>
+                                       -- Read state already set to R_STATUS in previous cycle
+                                       case d(7 downto 0) is
+                                               -- Program Enhanced Configuration Register
+                                               when x"04" =>
+                                                       ecr_reg <= a(ecr_reg'high downto 0);
+                                                       -- Datasheet says it returns to read-array mode
+                                                       cur_read_state <= R_ARRAY;
+
+                                               -- Lock Block
+                                               when x"01" =>
+                                                       -- address in a
+                                                       if sts_ready = '0' then
+                                                               -- Cannot program when WSM is busy
+                                                               sts_error_code <= "11";
+                                                       elsif sts_susp_erase = '1' or sts_susp_prog = '1' then
+                                                               -- Cannot program when erase or program suspended
+                                                               sts_error_code <= "11";
+                                                       else
+                                                               lock_address <= a;
+                                                               next_wsm_state <= W_LOCK_PROG;
+                                                               wsm_trigger <= '1';
+                                                       end if;
+
+                                               -- Unlock Blocks
+                                               when x"d0" =>
+                                                       if sts_ready = '0' then
+                                                               -- Cannot erase when WSM is busy
+                                                               sts_error_code <= "11";
+                                                       elsif sts_susp_erase = '1' or sts_susp_prog = '1' then
+                                                               -- Cannot erase when erase or program suspended
+                                                               sts_error_code <= "11";
+                                                       else
+                                                               next_wsm_state <= W_LOCK_ERASE;
+                                                               wsm_trigger <= '1';
+                                                       end if;
+
+                                               when others =>
+                                                       sts_error_code <= "11";
+                                       end case;
+                                       cur_cui_state <= C_IDLE;
+
+                               when others =>
+                                       cur_cui_state <= C_IDLE;
+                       end case;
+               end if;
+       end process;
+
+       -- Write state machine is responsible for checking:
+       -- * Vpen state
+       -- * valid offset/address
+       -- * lock protection
+       -- * checking wsm_trigger and next_wsm_state to begin operations
+       -- * checking sts_susp_* state to suspend operations
+       wsm: process (rp_n, vpen, sts_susp_erase, sts_susp_prog, wsm_trigger, cur_wsm_state, next_wsm_state)
+               variable otp_prog_idx: integer;
+               variable prog_buf_addr: integer;
+       begin
+               if rp_n = '0' then
+                       -- TODO: hold ready low for some time after reset
+                       cur_wsm_state <= W_READY;
+                       sts_ready <= '1';
+                       sts_susp_erase <= '0';
+                       sts_error_code <= "00";
+                       sts_error_volt <= '0';
+                       sts_susp_prog <= '0';
+                       sts_error_lock <= '0';
+               end if;
+
+               sts_ready <= '0';  -- Default to not-ready
+
+               -- Process triggers from the CUI FSM
+               if rising_edge(wsm_trigger) then
+                       cur_wsm_state <= next_wsm_state;
+               end if;
+
+               case cur_wsm_state is
+                       when W_READY =>
+                               sts_ready <= '1';  -- Ready status only in ready state
+
+                       when W_PROG =>
+                               -- Abort with voltage error if Vpen drops
+                               sts_ready <= '0';
+                               if vpen = '0' then
+                                       sts_error_code(0) <= '1';
+                                       sts_error_volt <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- Suspend programming operation when signaled
+                               if sts_susp_prog = '1' then
+                                       cur_wsm_state <= W_READY after T_WHRH1;  -- T_EHRH1
+                               end if;
+                               -- Check lock protection bit for block
+                               if lock_reg(block_index_of(prog_address)) = '1' then
+                                       sts_error_code(0) <= '1';
+                                       sts_error_lock <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               flash_array(block_index_of(prog_address))(word_index_of(prog_address)) <=
+                                       flash_array(block_index_of(prog_address))(word_index_of(prog_address))
+                                       and prog_word;
+                               cur_wsm_state <= W_READY after T_WHQV3;  -- T_EHQV3
+
+                       when W_BUF_PROG =>
+                               -- Abort with voltage error if Vpen drops
+                               if vpen = '0' then
+                                       sts_error_code(0) <= '1';
+                                       sts_error_volt <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- Suspend programming operation when signaled
+                               if sts_susp_prog = '1' then
+                                       cur_wsm_state <= W_READY after T_WHRH1;  -- T_EHRH1
+                               end if;
+                               -- Check lock protection bit for block
+                               if lock_reg(block_index_of(prog_address)) = '1' then
+                                       sts_error_code(0) <= '1';
+                                       sts_error_lock <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- TODO: write zeroes
+                               prog_buf_addr := to_integer(unsigned(prog_address));
+                               for addr in prog_buf_addr to prog_buf_addr + prog_count + 1 loop
+                                       flash_array(block_index_of(std_logic_vector(to_unsigned(addr, 24))))(word_index_of(std_logic_vector(to_unsigned(addr, 24)))) <=
+                                               flash_array(block_index_of(std_logic_vector(to_unsigned(addr, 24))))(word_index_of(std_logic_vector(to_unsigned(addr, 24))))
+                                               and prog_buf(addr mod 32);
+                               end loop;
+                               -- TODO: get correct timing
+                               cur_wsm_state <= W_READY after T_WHQV3;  -- T_EHQV3
+
+                       when W_ERASE =>
+                               -- Abort with voltage error if Vpen drops
+                               if vpen = '0' then
+                                       sts_error_code(1) <= '1';
+                                       sts_error_volt <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- Suspend erase operation when signaled
+                               if sts_susp_erase = '1' then
+                                       cur_wsm_state <= W_READY after T_WHRH;  -- T_EHRH
+                               end if;
+                               -- Check lock protection bit for block
+                               if lock_reg(block_index_of(erase_address)) = '1' then
+                                       sts_error_code(1) <= '1';
+                                       sts_error_lock <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- Erase block and return to ready
+                               flash_array(block_index_of(erase_address)) <= (others => (others => '1'));
+                               cur_wsm_state <= W_READY after T_WHQV4;  -- T_EHQV4
+
+                       when W_LOCK_PROG =>
+                               -- Abort with voltage error if Vpen drops
+                               if vpen = '0' then
+                                       sts_error_code(0) <= '1';
+                                       sts_error_volt <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- Write lock bit and return to ready
+                               lock_reg(block_index_of(lock_address)) <= '1';
+                               cur_wsm_state <= W_READY after T_WHQV5; -- T_EHQV5
+
+                       when W_LOCK_ERASE =>
+                               -- Abort with voltage error if Vpen drops
+                               if vpen = '0' then
+                                       sts_error_code(1) <= '1';
+                                       sts_error_volt <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- Erase all lock bits and return to ready
+                               lock_reg <= (others => '0');
+                               cur_wsm_state <= W_READY after T_WHQV6;  -- T_EHQV6
+
+                       when W_OTP_PROG =>
+                               -- Abort with voltage error if Vpen drops
+                               if vpen = '0' then
+                                       sts_error_code(0) <= '1';
+                                       sts_error_volt <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- Check for out-of-range address
+                               otp_prog_idx := to_integer(unsigned(otp_prog_offset(otp_prog_offset'high downto 1)));
+                               if otp_prog_idx < 16#80# or otp_prog_idx > 16#88# then
+                                       sts_error_code(0) <= '1';
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+                               -- Check for protection bits
+                               if otp_prog_idx >= 16#81# and otp_prog_idx <= 16#84# then
+                                       if plr_reg(0) = '0' then
+                                               sts_error_code(0) <= '1';
+                                               sts_error_lock <= '1';
+                                               cur_wsm_state <= W_READY after T_STS;
+                                       end if;
+                               end if;
+                               if otp_prog_idx >= 16#85# and otp_prog_idx <= 16#88# then
+                                       if plr_reg(1) = '0' then
+                                               sts_error_code(0) <= '1';
+                                               sts_error_lock <= '1';
+                                               cur_wsm_state <= W_READY after T_STS;
+                                       end if;
+                               end if;
+                               -- Write zeroes and return to ready
+                               if otp_prog_idx = 16#80# then
+                                       plr_reg <= plr_reg and otp_prog_data;
+                               else
+                                       otp_prog_idx := otp_prog_idx - 16#81#;
+                                       otp_reg(otp_prog_idx) <= otp_reg(otp_prog_idx) and otp_prog_data;
+                               end if;
+                               cur_wsm_state <= W_READY after T_WHQV5;  -- Best guess for timing
+
+                       when W_RESUME =>
+                               if sts_susp_prog = '1' then
+                                       -- Precedence goes to resuimg programming operation
+                                       sts_susp_prog <= '0';
+                                       cur_wsm_state <= W_PROG after T_STS;
+                               elsif sts_susp_erase = '1' then
+                                       -- Resume erase operation if no programming operation pending
+                                       sts_susp_erase <= '0';
+                                       cur_wsm_state <= W_ERASE after T_STS;
+                               else
+                                       -- Nothing to resume, return to ready
+                                       cur_wsm_state <= W_READY after T_STS;
+                               end if;
+
+                       when others =>
+                               cur_wsm_state <= W_READY after T_STS;
+               end case;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Check for erroneous usages
+
+       -- Should not try to read and write at the same time
+       process (oe_n, we_n)
+       begin
+               assert not (oe_n = '0' and we_n = '0')
+                       report "OE# and WE# should never be enabled simultaneously."
+                       severity error;
+       end process;
+
+       -- Address must be valid for a full read or write cycle
+       R1: process (oe_n, a(23 downto 4))
+       begin
+               -- FIXME: Check more address bits if in non-array or 4-word array modes
+               if a(23 downto 4)'event and oe_n = '0' then
+                       assert a(23 downto 4)'last_event >= T_AVAV;
+                               report "T_AVAV not met (Read/Write Cycle Time)"
+                               severity error;
+               end if;
+       end process;
+
+       -- BYTE# cannot change more than T_ELFL/T_ELFH after CEx goes low
+       R11: process (byte_n, internal_ce_n)
+       begin
+               if byte_n'event and internal_ce_n = '0' then
+                       assert internal_ce_n'last_event <= T_ELFL;
+                               report "T_ELFL/H not met (CEx Low to BYTE# High or Low)"
+                               severity error;
+               end if;
+       end process;
+
+       -- WE# and CEx cannot become active less than T_PHWL/EL after RP# goes high
+       W1: process (rp_n, we_n, internal_ce_n)
+       begin
+               if rp_n = '1' then
+                       if falling_edge(we_n) then
+                               assert rp_n'last_event >= T_PHWL
+                                       report "T_PHWL not met (RP# High Recovery to WE# Going Low)"
+                                       severity error;
+                       end if;
+                       if falling_edge(internal_ce_n) then
+                               assert rp_n'last_event >= T_PHEL
+                                       report "T_PHEL not met (RP# High Recovery to CEx Going Low)"
+                                       severity error;
+                       end if;
+               end if;
+       end process;
+
+       -- CEx and WE# may have set-up timing constraints relative to each other
+       W2: process (we_n, internal_ce_n)
+       begin
+               if falling_edge(we_n) and internal_ce_n = '0' then
+                       assert internal_ce_n'last_event >= T_ELWL
+                               report "T_ELWL not met (CEx Low to WE# Going Low)"
+                               severity error;
+               end if;
+               if falling_edge(internal_ce_n) and we_n = '0' then
+                       assert we_n'last_event >= T_WLEL
+                               report "T_WLEL not met (WE# Low to CEx Going Low)"
+                               severity error;
+               end if;
+       end process;
+
+       -- WE# has a minimum low pulse width
+       W3: process (we_n)
+       begin
+               if rising_edge(we_n) then
+                       --assert we_n'last_event >= T_WP
+                       assert we_n'delayed(T_WP) = '0'
+                               report "T_WP not met (Write Pulse Width)"
+                               severity error;
+               end if;
+       end process;
+
+       -- Data-in set-up time (no need to use internal_we_n here)
+       W4: process (internal_we_n, d)
+       begin
+               if rising_edge(internal_we_n) then
+                       -- T_DVEH and T_DVWH are the same in the datasheet, so they probably
+                       -- refer to the internal write-latch signal composed of CEx and WE#.
+                       -- Checking against the internal signal prevents us from checking
+                       -- timing between our own output and CEx during write cycles.
+                       assert d'delayed'last_event >= T_DVWH  -- Also T_DVEH
+                               report "T_DVWH/T_DVEH not met (Data Setup to WE#/CEx Going High)" & time'image(d'last_event)
+                               severity error;
+               end if;
+               --if rising_edge(we_n) then
+               --      assert d'last_event >= T_DVWH
+               --              report "T_DVWH not met (Data Setup to WE# Going High)"
+               --              severity error;
+               --end if;
+               --if rising_edge(internal_ce_n) then
+               --      -- Have seen some false-positives where d'last_event = 0 ps, but d'event = False
+               --      -- Delaying 1 ps will get past any event in the current simulation cycle
+               --      assert d'delayed(1 ps)'last_event >= T_DVEH
+               --              report "T_DVEH not met (Data Setup to CEx Going High)"
+               --              severity error;
+               --end if;
+       end process;
+
+       -- Address set-up time (no need to use internal_we_n here)
+       W5: process (internal_we_n, a)
+       begin
+               if rising_edge(internal_we_n) then
+                       -- T_AVEH and T_AVWH are the same in the datasheet, so they probably
+                       -- refer to the internal write-latch signal composed of CEx and WE#.
+                       -- Checking against the internal signal prevents us from checking
+                       -- timing against CEx during read cycles.
+                       assert a'last_event >= T_AVWH  -- Also T_DVEH
+                               report "T_AVWH/T_DVEH not met (Data Setup to WE#/CEx Going High)"
+                               severity error;
+               end if;
+               --if rising_edge(we_n) then
+               --      assert a'last_event >= T_AVWH
+               --              report "T_AVWH not met (Address Setup to WE# Going High)"
+               --              severity error;
+               --end if;
+               --if rising_edge(internal_ce_n) then
+               --      assert a'last_event >= T_AVEH
+               --              report "T_AVEH not met (Address Setup to CEx Going High)"
+               --              severity error;
+               --end if;
+       end process;
+
+       -- CEx and WE# may have hold timing constraints relative to each other
+       W6: process (we_n, internal_ce_n)
+       begin
+               if rising_edge(we_n) and internal_ce_n = '1' then
+                       assert internal_ce_n'last_event >= T_EHWH
+                               report "T_EHWH not met (WE# Hold from CEx High)"
+                               severity error;
+               end if;
+               if rising_edge(internal_ce_n) and we_n = '1' then
+                       assert we_n'last_event >= T_WHEH
+                               report "T_WHEH not met (CEx Hold from WE# High)"
+                               severity error;
+               end if;
+       end process;
+
+       -- Data-in hold time
+       W7: process (internal_we_n, d)
+       begin
+               if d'event and internal_we_n = '1' then
+                       assert internal_we_n'last_event >= T_WHDX  -- T_EHDX
+                               report "T_W/EHDX not met (Data Hold from WE# (CEx) High)"
+                               severity error;
+               end if;
+       end process;
+
+       -- Address hold time
+       W8: process (internal_we_n, a)
+       begin
+               if a'event and internal_we_n = '1' then
+                       assert internal_we_n'last_event >= T_WHAX  -- T_EHAX
+                               report "T_W/EHAX not met (Address Hold from WE# (CEx) High)"
+                               severity error;
+               end if;
+       end process;
+
+       -- WE# has a minimum high pulse width
+       W9: process (we_n)
+       begin
+               if falling_edge(we_n) then
+                       --assert we_n'last_event >= T_WPH
+                       assert we_n'delayed(T_WPH) = '1'
+                               report "T_WPH not met (Write Pulse Width High)"
+                               severity error;
+               end if;
+       end process;
+
+       -- Vpen set-up time before write latch (don't need to use internal_we_n here)
+       W11: process (we_n, internal_ce_n, vpen)
+       begin
+               if rising_edge(we_n) and vpen = '1' then
+                       assert vpen'last_event >= T_VPWH
+                               report "T_VPWH not met (Vpen Setup to WE# High)"
+                               severity error;
+               end if;
+               if rising_edge(internal_ce_n) and vpen = '1' then
+                       assert vpen'last_event >= T_VPEH
+                               report "T_VPEH not met (Vpen Setup to CEx High)"
+                               severity error;
+               end if;
+       end process;
+
+       -- Write recovery before Read
+       W12: process (internal_we_n, oe_n)
+       begin
+               if falling_edge(oe_n) and internal_we_n = '1' then
+                       assert internal_we_n'last_event >= T_WHGL  -- T_EHGL
+                               report "T_W/EHGL not met (Write Recovery before Read)"
+                               severity error;
+               end if;
+       end process;
+
+       -- Vpen hold time
+       W15: process (vpen)
+       begin
+               -- TODO: T_QVVL never shown on any waveform diagrams
+       end process;
+
+       -- RP# pulse time
+       P1: process (rp_n)
+       begin
+               if rising_edge(rp_n) then
+                       -- If the simulations tarts with rp_n = '0' then last_event won't work
+                       -- assert rp_n'last_event >= T_PLPH
+                       assert rp_n'delayed(T_PLPH) = '0'
+                               report "T_PLPH not met (RP# Pulse Low Time)"
+                               severity error;
+               end if;
+       end process;
+
+end behavioral;
diff --git a/libraries/simulated/mt45w8mw16bgx.vhd b/libraries/simulated/mt45w8mw16bgx.vhd
new file mode 100644 (file)
index 0000000..1d88889
--- /dev/null
@@ -0,0 +1,333 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library simulated;
+
+
+entity mt45w8mw16bgx is
+       port (
+               a:     in    std_logic_vector(22 downto 0);
+               dq:    inout std_logic_vector(15 downto 0);
+               clk:   in    std_logic;
+               adv_n: in    std_logic;
+               cre:   in    std_logic;
+               ce_n:  in    std_logic;
+               oe_n:  in    std_logic;
+               we_n:  in    std_logic;
+               lb_n:  in    std_logic;
+               ub_n:  in    std_logic;
+               rwait: out   std_logic
+       );
+end mt45w8mw16bgx;
+
+
+architecture behavioral of mt45w8mw16bgx is
+
+       -- Timings (-708/80MHz)
+       constant T_AA:   time := 70 ns;   -- Address access time
+       constant T_AADV: time := 70 ns;   -- ADV# access time
+       constant T_ABA:  time := 46.5 ns; --
+       constant T_ACLK: time := 9 ns;    --
+       constant T_APA:  time := 20 ns;   -- Page access time
+       constant T_AS:   time := 0 ns;    --
+       constant T_AVH:  time := 2 ns;    -- Address hold from ADV# HIGH
+       constant T_AVS:  time := 5 ns;    -- Address setup to ADV# HIGH
+       constant T_AW:   time := 70 ns;   --
+       constant T_BA:   time := 70 ns;   -- LB#/UB# access time
+       constant T_BHZ:  time := 8 ns;    -- LB#/UB# disable to DQ High-Z output
+       constant T_BLZ:  time := 10 ns;   -- LB#/UB# enable to Low-Z output
+       constant T_BOE:  time := 20 ns;   --
+       constant T_BW:   time := 70 ns;   --
+       constant T_CBPH: time := 6 ns;    --
+       constant T_CEM:  time := 4 us;    -- Maximum CE# pulse width
+       constant T_CEW:  time := 7.5 ns;  -- CE# LOW to WAIT valid
+       constant T_CLK:  time := 12.5 ns; --
+       constant T_CO:   time := 70 ns;   -- Chip select access time
+       constant T_CPH:  time := 5 ns;    --
+       constant T_CSP:  time := 4 ns;    --
+       constant T_CVS:  time := 7 ns;    -- CE# LOW to ADV# HIGH
+       constant T_CW:   time := 70 ns;   --
+       constant T_DH:   time := 0 ns;    --
+       constant T_DPD:  time := 10 us;   --
+       constant T_DPDX: time := 10 us;   --
+       constant T_DW:   time := 20 ns;   --
+       constant T_HD:   time := 2 ns;    --
+       constant T_HZ:   time := 8 ns;    -- Chip disable to DQ and WAIT High-Z output
+       constant T_KHKL: time := 1.8 ns;  --
+       constant T_KHTL: time := 9 ns;    --
+       constant T_KOH:  time := 2 ns;    --
+       constant T_KP:   time := 4 ns;    --
+       constant T_LZ:   time := 10 ns;   -- Chip enable to Low-Z output
+       constant T_OE:   time := 20 ns;   -- Output enable to valid output
+       constant T_OH:   time := 5 ns;    -- Output hold from address change
+       constant T_OHZ:  time := 8 ns;    -- Output disable to DQ High-Z output
+       constant T_OLZ:  time := 3 ns;    -- Output enable to Low-Z output
+       constant T_OW:   time := 5 ns;    --
+       constant T_PC:   time := 20 ns;   -- Page READ cycle time
+       constant T_PU:   time := 150 us;  --
+       constant T_RC:   time := 70 ns;   -- READ cycle time
+       constant T_SP:   time := 3 ns;    --
+       constant T_VP:   time := 5 ns;    -- ADV# pulse width LOW
+       constant T_VS:   time := 70 ns;   --
+       constant T_WC:   time := 70 ns;   --
+       constant T_WHZ:  time := 8 ns;    --
+       constant T_WP:   time := 45 ns;   --
+       constant T_WPH:  time := 10 ns;   --
+       constant T_WR:   time := 0 ns;    --
+
+       -- Internal types
+       type word_array is array(natural range <>) of std_logic_vector(15 downto 0);
+
+       -- Data array read signals
+       signal array_reg:  word_array(15 downto 0) := (others => (others => '1'));
+       signal array_word: std_logic_vector(15 downto 0);
+       signal read_word:  std_logic_vector(15 downto 0);
+       signal xmask_addr: std_logic;
+       signal xmask_ce_n: std_logic;
+       signal xmask_oe_n: std_logic;
+       signal xmask_ub_n: std_logic;
+       signal xmask_lb_n: std_logic;
+       signal xmask_hi:   std_logic;
+       signal xmask_lo:   std_logic;
+       signal zmask_ce_n: std_logic;
+       signal zmask_oe_n: std_logic;
+       signal zmask_ub_n: std_logic;
+       signal zmask_lb_n: std_logic;
+       signal zmask_hi:   std_logic;
+       signal zmask_lo:   std_logic;
+
+       -- Data array write signals
+       signal internal_we_ub_n: std_logic;
+       signal internal_we_lb_n: std_logic;
+
+       -- Configuration registers
+       signal bcr_reg:  std_logic_vector(15 downto 0) := x"9d1f";
+       signal rcr_reg:  std_logic_vector(15 downto 0) := x"0010";
+       signal didr_reg: std_logic_vector(15 downto 0) := x"0343";
+
+begin
+
+       ----------------------------------------------------------------------------
+       -- Asynchronous array reads
+
+       -- Look up value from memory array, delay to allow for hold time
+       array_word <= array_reg(to_integer(unsigned(a))) after T_HZ;  -- T_OHZ, T_BHZ
+
+       -- Generate mask for periods of time with invalid data
+       -- FIXME: this may break hold time
+       xm_addr: entity simulated.is_unstable_v generic map (T => T_AA, T_HOLD => T_OH)
+               port map (sig_in => a, sig_out => xmask_addr);
+       xm_ce: entity simulated.is_unstable generic map (T => T_CO, T_HOLD => T_HZ)
+               port map (sig_in => ce_n, sig_out => xmask_ce_n);
+       xm_oe: entity simulated.is_unstable generic map (T => T_OE, T_HOLD => T_OHZ)
+               port map (sig_in => oe_n, sig_out => xmask_oe_n);
+       xm_ub: entity simulated.is_unstable generic map (T => T_BA, T_HOLD => T_BHZ)
+               port map (sig_in => ub_n, sig_out => xmask_ub_n);
+       xm_lb: entity simulated.is_unstable generic map (T => T_BA, T_HOLD => T_BHZ)
+               port map (sig_in => lb_n, sig_out => xmask_lb_n);
+       xmask_hi <= xmask_addr or xmask_ce_n or xmask_oe_n or xmask_ub_n;
+       xmask_lo <= xmask_addr or xmask_ce_n or xmask_oe_n or xmask_lb_n;
+
+       -- Invalidate data for some periods of time
+       read_word(15 downto 8) <= (others => 'X') when xmask_hi = '1' else array_word(15 downto 8);
+       read_word( 7 downto 0) <= (others => 'X') when xmask_lo = '1' else array_word( 7 downto 0);
+
+       -- Generate mask for periods of time where data bus is high-z
+       zm_ce: entity simulated.delay_edges generic map (D_RISE => T_HZ,  D_FALL => T_LZ)
+               port map (sig_in => ce_n, sig_out => zmask_ce_n);
+       zm_oe: entity simulated.delay_edges generic map (D_RISE => T_OHZ, D_FALL => T_OLZ)
+               port map (sig_in => oe_n, sig_out => zmask_oe_n);
+       zm_ub: entity simulated.delay_edges generic map (D_RISE => T_BHZ, D_FALL => T_BLZ)
+               port map (sig_in => ub_n, sig_out => zmask_ub_n);
+       zm_lb: entity simulated.delay_edges generic map (D_RISE => T_BHZ, D_FALL => T_BLZ)
+               port map (sig_in => lb_n, sig_out => zmask_lb_n);
+       zmask_hi <= zmask_ce_n or zmask_oe_n or zmask_ub_n;
+       zmask_lo <= zmask_ce_n or zmask_oe_n or zmask_lb_n;
+
+       -- Generate output data signals
+       dq(15 downto 8) <= (others => 'Z') when zmask_hi = '1' or we_n = '0'
+                                          else read_word(15 downto 8);
+       dq( 7 downto 0) <= (others => 'Z') when zmask_lo = '1' or we_n = '0'
+                                          else read_word( 7 downto 0);
+
+
+       ----------------------------------------------------------------------------
+       -- Asynchronous array writes
+
+       -- Internal signals to trigger latching of data
+       internal_we_ub_n <= ce_n or we_n or ub_n;
+       internal_we_lb_n <= ce_n or we_n or lb_n;
+
+       -- Latch data on rising edge of write signal
+       process (internal_we_ub_n, internal_we_lb_n)
+       begin
+               -- Changing (or tristating) DQ at the same moment as the write rising-edge should
+               -- be allowed (T_DH, data hold time, = 0 ns), but this breaks in ISim 14.7, hence
+               -- the 1 ps delay.
+               if rising_edge(internal_we_ub_n) then
+                       array_reg(to_integer(unsigned(a)))(15 downto 8) <= dq(15 downto 8)'delayed(1 ps);
+               end if;
+
+               if rising_edge(internal_we_lb_n) then
+                       array_reg(to_integer(unsigned(a)))( 7 downto 0) <= dq( 7 downto 0)'delayed(1 ps);
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Check for erroneous usage
+
+       -- TODO: check T_AVH, T_AVS, T_CVS, T_VP, T_AS
+       assert adv_n = '0'
+               report "ADV# not yet supported"
+               severity error;
+
+       assert clk = '0'
+               report "Synchronous operation not yet supported"
+               severity error;
+
+
+       -- Should (probably) not assert oe_n and we_n at the same time
+       process (oe_n, we_n)
+       begin
+               assert not (oe_n = '0' and we_n = '0')
+                       report "OE# and WE# are active simultaneously (write takes precedence)"
+                       severity warning;
+       end process;
+
+       -- Maximum CE# pulse width (to allow internal refresh)
+       process (ce_n)
+       begin
+               if rising_edge(ce_n) then
+                       assert ce_n'last_event < T_CEM
+                               report "T_CEM not met (Maximum CE# pulse width)"
+                               severity error;
+               end if;
+       end process;
+
+
+       -- Write recovery time (address hold time)
+       process (a, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if a'event then
+                       if internal_we_ub_n = '1' then
+                               assert internal_we_ub_n'last_event >= T_WR
+                                       report "T_WR not met (Write recovery time)"
+                                       severity error;
+                       end if;
+                       if internal_we_lb_n = '1' then
+                               assert internal_we_lb_n'last_event >= T_WR
+                                       report "T_WR not met (Write recovery time)"
+                                       severity error;
+                       end if;
+               end if;
+       end process;
+
+       -- Address valid to end of WRITE
+       process (a, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if rising_edge(internal_we_ub_n) then
+                       assert a'last_event >= T_AW
+                               report "T_AW not met (Address valid to end of WRITE)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       assert a'last_event >= T_AW
+                               report "T_AW not met (Address valid to end of WRITE)"
+                               severity error;
+               end if;
+       end process;
+
+       -- Chip enable to end of WRITE
+       process (ce_n, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if rising_edge(internal_we_ub_n) then
+                       assert ce_n'delayed'delayed'last_event >= T_CW
+                               report "T_CW not met (Chip enable to end of WRITE)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       assert ce_n'delayed'delayed'last_event >= T_CW
+                               report "T_CW not met (Chip enable to end of WRITE)"
+                               severity error;
+               end if;
+       end process;
+
+       -- CE# HIGH between subsequent async operations
+       process (ce_n)
+       begin
+               if falling_edge(ce_n) then
+                       assert ce_n'delayed'last_event >= T_CPH
+                               report "T_CPH not met (CE# HIGH between subsequent async operations)"
+                               severity error;
+               end if;
+       end process;
+
+       -- LB#/UB# select to end of WRITE
+       process (ub_n, lb_n, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if rising_edge(internal_we_ub_n) then
+                       assert ub_n'last_event >= T_BW
+                               report "T_BW not met (UB# select to end of WRITE)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       assert lb_n'last_event >= T_BW
+                               report "T_BW not met (LB# select to end of WRITE)"
+                               severity error;
+               end if;
+       end process;
+
+       -- WRITE pulse width
+       process (we_n, internal_we_ub_n, internal_we_lb_n)
+               variable t: time;
+       begin
+               -- Datasheet specifies WE# HIGH pulse width strictly on WE# signal
+               -- FIXME: should this be strictly on internal we signals?
+               if falling_edge(we_n) then
+                       assert we_n'delayed'last_event >= T_WPH
+                               report "T_WPH not met (WRITE pulse width HIGH)"
+                               severity error;
+               end if;
+               -- Datasheet specifies WE# LOW pulse width against whatever signal triggers write
+               -- FIXME: should this be strictly on internal we signals?
+               if rising_edge(internal_we_ub_n) then
+                       assert we_n'delayed'delayed'last_event >= T_WP
+                               report "T_WP not met (WRITE pulse width)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       t := we_n'delayed'delayed'last_event;
+                       assert we_n'delayed'delayed'last_event >= T_WP
+                               report "T_WP not met (WRITE pulse width)" & time'image(t)
+                               severity error;
+               end if;
+       end process;
+
+       -- Data setup and hold times
+       process (dq, internal_we_ub_n, internal_we_lb_n)
+       begin
+               if rising_edge(internal_we_ub_n) then
+                       assert dq(15 downto 8)'delayed'delayed'last_event >= T_DW
+                               report "T_DW not met (Data upper byte WRITE setup time)"
+                               severity error;
+               end if;
+               if rising_edge(internal_we_lb_n) then
+                       assert dq(7 downto 0)'delayed'delayed'last_event >= T_DW
+                               report "T_DW not met (Data lower byte WRITE setup time)"
+                               severity error;
+               end if;
+               if dq(15 downto 8)'event then
+                       assert internal_we_ub_n'last_event >= T_DH
+                               report "T_DH not met (Data upper byte HOLD from WRITE time)"
+                               severity error;
+               end if;
+               if dq(7 downto 0)'event then
+                       assert internal_we_lb_n'last_event >= T_DH
+                               report "T_DH not met (Data lower byte HOLD from WRITE time)"
+                               severity error;
+               end if;
+       end process;
+
+end behavioral;
diff --git a/libraries/simulated/ps2_device.vhd b/libraries/simulated/ps2_device.vhd
new file mode 100644 (file)
index 0000000..fe79f68
--- /dev/null
@@ -0,0 +1,209 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+use ieee.std_logic_misc.all;
+
+
+entity ps2_device is
+       port (
+               tx_byte:  in    std_logic_vector(7 downto 0);
+
+               ps2_clk:  inout std_logic;
+               ps2_data: inout std_logic
+       );
+end ps2_device;
+
+
+architecture behavioral of ps2_device is
+
+       type state_t is (
+               S_IDLE,     S_INHIBIT,
+               S_TX_BIT_L, S_TX_BIT_H, S_TX_STOP_L, S_TX_STOP_H,
+               S_RX_BIT_L, S_RX_BIT_H, S_RX_STOP_L
+       );
+
+       -- Timings from http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
+       -- Slowest timing:
+       --constant T1: time := 25 us;  -- Time from DATA transition to falling edge of CLK
+       --constant T2: time := 45 us;  -- Time from rising edge of CLK to DATA transition
+       --constant T3: time := 50 us;  -- Duration of CLK inactive (Tx)
+       --constant T4: time := 50 us;  -- Duration of CLK active   (Tx)
+       --constant T5: time := 50 us;
+       --constant T7: time := 50 us;  -- Duration of CLK inactive (Rx)
+       --constant T8: time := 50 us;  -- Duration of CLK active   (Rx)
+       --constant T9: time := 25 us;  -- Time after CLK rise to sample
+
+       -- Fastest timing:
+       constant T1: time := 5 us;   -- Time from DATA transition to falling edge of CLK
+       constant T2: time := 5 us;   -- Time from rising edge of CLK to DATA transition
+       constant T3: time := 30 us;  -- Duration of CLK inactive (Tx)
+       constant T4: time := 30 us;  -- Duration of CLK active   (Tx)
+       --constant T5: time := 0 us;
+       constant T7: time := 30 us;  -- Duration of CLK inactive (Rx)
+       constant T8: time := 30 us;  -- Duration of CLK active   (Rx)
+       constant T9: time := 5 us;   -- Time after CLK rise to sample
+
+       signal cur_state: state_t := S_IDLE;
+
+       signal tx_req:    boolean;
+
+       -- CLK and DATA that the device is *trying* to drive, can be overridden by the host
+       signal dev_clk:   std_logic := '1';
+       signal dev_data:  std_logic := '1';
+
+       -- For detecting when the host overrides device's CLK and DATA
+       signal inhibit:   std_logic;
+       signal host_tx:   std_logic;
+
+       signal test_sample: std_logic;
+
+       signal data: std_logic_vector(10 downto 0);
+       signal i: integer;
+
+begin
+
+       test_sample <= ps2_data'delayed(T8 - T9);
+
+       process (cur_state, inhibit, host_tx, tx_req, tx_byte)
+               --variable data: std_logic_vector(10 downto 0);
+               --variable i: integer;
+               variable temp: std_logic;
+       begin
+               if inhibit'event then
+                       if inhibit = '1' then
+                               -- Abort any transmissions when inhibited
+                               if cur_state /= S_IDLE then
+                                       report "PS2 device inhibited (transmission aborted)";
+                               else
+                                       report "PS2 device inhibited";
+                               end if;
+                               if cur_state = S_TX_STOP_L or cur_state = S_TX_STOP_H then
+                                       -- FIXME: prevent inhibition during the stop bit, reporting is fine for now
+                                       report "PS2 device inhibited during stop bit" severity error;
+                               end if;
+                               cur_state <= S_INHIBIT;
+                       else
+                               report "PS2 device uninhibited";
+                               if host_tx = '1' then
+                                       i <= 0;
+                                       data <= (others => '0');
+                                       cur_state <= S_RX_BIT_L after T8;
+                               else
+                                       cur_state <= S_IDLE;
+                               end if;
+                       end if;
+               elsif tx_req'event and tx_req then
+                       report "PS2 device beginning transmission";
+                       data <= '1' & '1' & (not xor_reduce(tx_byte)) & tx_byte;
+                       i <= 0;
+                       dev_data <= '0';
+                       cur_state <= S_TX_BIT_L after T1;
+               elsif host_tx'event and host_tx = '1' then
+                       if not (cur_state = S_IDLE or cur_state = S_INHIBIT or
+                               cur_state = S_RX_BIT_L or cur_state = S_RX_BIT_H or cur_state = S_RX_STOP_L) then
+                               report "PS2 device saw unexpected drop in DATA line" severity error;
+                       elsif cur_state = S_IDLE then
+                               -- http://www.mcamafia.de/pdf/ibm_hitrc07.pdf *implies* that the
+                               -- device should still receive data without the host pulling the
+                               -- clock low first, but this is unusual and could cause problems
+                               -- if the device is about to begin a transmission.
+                               report "PS2 device saw drop in DATA line while not inhibited" severity warning;
+                               i <= 0;
+                               data <= (others => '0');
+                               cur_state <= S_RX_BIT_L after T8;
+                       end if;
+               elsif cur_state'event then
+                       -- Self-driving state machine, can be pushed into states by the rest of the process
+                       case cur_state is
+                               when S_IDLE =>
+                                       dev_clk <= '1';
+                                       dev_data <= '1';
+
+                               when S_TX_BIT_L =>
+                                       dev_clk <= '0';
+                                       if i < 9 then
+                                               cur_state <= S_TX_BIT_H after T3;
+                                       else
+                                               cur_state <= S_TX_STOP_H after T3;
+                                       end if;
+
+                               when S_TX_BIT_H =>
+                                       dev_clk <= '1';
+                                       dev_data <= data(i) after T2;
+                                       i <= i + 1;
+                                       cur_state <= S_TX_BIT_L after T4;
+
+                               when S_TX_STOP_H =>
+                                       dev_clk <= '1';
+                                       dev_data <= '1' after T2;
+                                       cur_state <= S_TX_STOP_L after T4;
+
+                               when S_TX_STOP_L =>
+                                       dev_clk <= '0';
+                                       cur_state <= S_IDLE after T3;
+
+                               when S_INHIBIT =>
+                                       dev_clk <= '1';
+                                       dev_data <= '1';
+
+                               when S_RX_BIT_L =>
+                                       dev_clk <= '0';
+                                       data(i) <= ps2_data'delayed(T8 - T9);  -- Reach back into the previous clock high period
+                                       i <= i + 1;
+                                       cur_state <= S_RX_BIT_H after T7;
+
+                               when S_RX_BIT_H =>
+                                       dev_clk <= '1';
+                                       if i <= 9 then
+                                               cur_state <= S_RX_BIT_L after T8;
+                                       else
+                                               cur_state <= S_RX_STOP_L after T8;
+                                       end if;
+
+                               when S_RX_STOP_L =>
+                                       dev_clk <= '0';
+                                       dev_data <= '0';  -- Pull DATA low to acknowledge
+                                       data(i) <= ps2_data'delayed(T8 - T9);  -- Reach back into the previous clock high period
+                                       temp := ps2_data'delayed(T8 - T9);
+                                       report "PS2 device recieved " & integer'image(to_integer(unsigned(data(8 downto 1))));
+                                       -- Spec says device should continue clocking and request a
+                                       -- resend if it gets a zero stop bit, but that's already an
+                                       -- error condition so reporting is sufficient.
+                                       assert temp /= '0'
+                                               report "PS2 device got a 0 stop bit"
+                                               severity error;
+                                       assert xor_reduce(data(9 downto 1)) = '1'
+                                               report "PS2 device received wrong parity"
+                                               severity error;
+                                       cur_state <= S_IDLE after T7;
+
+                               when others =>
+                                       cur_state <= S_IDLE;
+                       end case;
+               end if;
+       end process;
+
+       -- Check for anything driving the CLK and DATA lines too hard
+       process (ps2_clk, ps2_data)
+       begin
+               if ps2_clk = '1' then
+                       report "PS2 clock line driven harder than expected" severity error;
+               end if;
+               if ps2_data = '1' then
+                       report "PS2 data line driven harder than expected" severity error;
+               end if;
+       end process;
+
+       -- Weaken clock and data high signals
+       ps2_clk  <= '0' when dev_clk  = '0' else 'H';
+       ps2_data <= '0' when dev_data = '0' else 'H';
+
+       -- Detect host signaling
+       -- Needs delayed dev_clk/data to account for "gate delay" from computing ps2_clk/data from dev_clk/data
+       inhibit <= '1' when ps2_clk  = '0' and dev_clk'delayed  = '1' else '0';
+       host_tx <= '1' when ps2_data = '0' and dev_data'delayed = '1' else '0';
+
+       -- Detect test process transmit request
+       tx_req <= not tx_byte'quiet;
+
+end behavioral;
diff --git a/libraries/simulated/tests/test_js28f128j3d75.vhd b/libraries/simulated/tests/test_js28f128j3d75.vhd
new file mode 100644 (file)
index 0000000..b84c0d1
--- /dev/null
@@ -0,0 +1,181 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library flash_js28f128;
+
+
+entity testbench_j28f128j3d75 is
+end testbench_j28f128j3d75;
+
+
+architecture behavior of testbench_js28f128j3d75 is
+
+       type op is (O_NONE, O_READ_ARRAY, O_READ_STATUS, O_PROG_A, O_PROG_B, O_MODE_ARRAY);
+
+       -- Flash signals
+       signal a: std_logic_vector(23 downto 0);
+       signal d: std_logic_vector(15 downto 0);
+       signal ce: std_logic_vector(2 downto 0);
+       signal rp_n: std_logic;
+       signal oe_n: std_logic;
+       signal we_n: std_logic;
+       signal sts: std_logic;
+       signal byte_n: std_logic;
+       signal vpen: std_logic;
+
+       -- Debug
+       signal cur_op: op;
+
+begin
+
+       uut: entity flash_js28f128.js28f128j3d75 port map (
+               a      => a,
+               d      => d,
+               ce     => ce,
+               rp_n   => rp_n,
+               oe_n   => oe_n,
+               we_n   => we_n,
+               sts    => sts,
+               byte_n => byte_n,
+               vpen   => vpen
+       );
+
+
+       tb: process
+       begin
+               -- Initial values
+               cur_op <= O_NONE;
+               a <= (others => '0');
+               d <= (others => 'Z');
+               ce <= (others => '1');
+               oe_n <= '1';
+               we_n <= '1';
+               byte_n <= '1';
+               vpen <= '1';
+
+               -- Reset
+               rp_n <= '1';
+               wait for 1 us;
+               rp_n <= '0';
+               wait for 25 us;
+               rp_n <= '1';
+               wait for 210 ns;
+
+               -- Wait for long BYTE# signal propagation time
+               wait for 1 ms;
+
+               -- Asynchronous array read
+               cur_op <= O_READ_ARRAY;
+               ce(0) <= '0';
+               oe_n <= '0';
+               a <= x"000000";
+               wait for 100 ns;
+               a <= x"000002";
+               wait for 100 ns;
+               a <= x"000004";
+               wait for 100 ns;
+               a <= x"000006";
+               wait for 100 ns;
+               a <= x"000008";
+               wait for 100 ns;
+               a <= x"00000a";
+               wait for 100 ns;
+               a <= x"00000c";
+               wait for 100 ns;
+               a <= x"00000e";
+               wait for 100 ns;
+               ce(0) <= '1';
+               oe_n <= '1';
+               cur_op <= O_NONE;
+
+               wait for 25 ns;
+
+               -- Program word
+               ce(0) <= '0';
+
+               cur_op <= O_PROG_A;
+               a <= x"000000";
+               d <= x"0040";
+               we_n <= '0';
+               wait for 60 ns;
+               we_n <= '1';
+               wait for 30 ns;
+
+               cur_op <= O_PROG_B;
+               a <= x"000000";
+               d <= x"abcd";
+               we_n <= '0';
+               wait for 60 ns;
+               we_n <= '1';
+               wait for 30 ns;
+
+               ce(0) <= '1';
+               d <= (others => 'Z');
+               cur_op <= O_NONE;
+               wait for 100 us;
+
+               -- Read status during program
+               cur_op <= O_READ_STATUS;
+               ce(0) <= '0';
+               oe_n <= '0';
+               a <= x"000000";
+               wait for 100 ns;
+               assert d = x"0000"
+                       report "Status read got wrong value: " & integer'image(to_integer(unsigned(d)))
+                       severity error;
+               oe_n <= '1';
+               ce(0) <= '1';
+               cur_op <= O_NONE;
+               wait for 25 ns;
+
+               -- Wait for program to complete
+               wait until sts /= '0';
+               wait for 25 ns;
+
+               -- Read status after program
+               cur_op <= O_READ_STATUS;
+               ce(0) <= '0';
+               oe_n <= '0';
+               a <= x"000000";
+               wait for 100 ns;
+               assert d = x"0080"
+                       report "Status read got wrong value: " & integer'image(to_integer(unsigned(d)))
+                       severity error;
+               oe_n <= '1';
+               ce(0) <= '1';
+               cur_op <= O_NONE;
+               wait for 25 ns;
+
+               -- Enter read-array mode
+               cur_op <= O_MODE_ARRAY;
+               a <= x"000000";
+               d <= x"00ff";
+               ce(0) <= '0';
+               we_n <= '0';
+               wait for 60 ns;
+               we_n <= '1';
+               wait for 30 ns;
+               ce(0) <= '1';
+               d <= (others => 'Z');
+               cur_op <= O_NONE;
+               wait for 25 ns;
+
+               -- Read flash array value
+               cur_op <= O_READ_ARRAY;
+               ce(0) <= '0';
+               oe_n <= '0';
+               a <= x"000000";
+               wait for 100 ns;
+               assert d = x"abcd"
+                       report "Array read got wrong value"
+                       severity error;
+               oe_n <= '1';
+               ce(0) <= '1';
+               cur_op <= O_NONE;
+               wait for 25 ns;
+
+               wait;
+       end process;
+
+end architecture;
diff --git a/libraries/simulated/tests/test_mt45w8mw16bgx.vhd b/libraries/simulated/tests/test_mt45w8mw16bgx.vhd
new file mode 100644 (file)
index 0000000..d045fb1
--- /dev/null
@@ -0,0 +1,92 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library simulated;
+
+
+entity testbench_ram is
+end testbench_ram;
+
+
+architecture behavior of testbench_ram is
+
+       signal addr: std_logic_vector(23 downto 0);  -- Extra bit to allow hex literals
+       signal data: std_logic_vector(15 downto 0);
+       signal ce_n: std_logic;
+       signal oe_n: std_logic;
+       signal we_n: std_logic;
+
+begin
+
+       tb: process
+       begin
+               addr <= x"000000";
+               data <= (others => 'Z');
+               ce_n <= '1';
+               oe_n <= '1';
+               we_n <= '1';
+               --wait for 150 us;  -- T_PU, initialization period
+               wait for 20 ns;
+
+               addr <= x"000000";
+               data <= x"dead";
+               ce_n <= '0';
+               we_n <= '0';
+               wait for 70 ns;
+               ce_n <= '1';
+               we_n <= '1';
+               data <= (others => 'Z');
+               wait for 10 ns;
+
+               addr <= x"000001";
+               data <= x"beef";
+               ce_n <= '0';
+               we_n <= '0';
+               wait for 70 ns;
+               ce_n <= '1';
+               we_n <= '1';
+               data <= (others => 'Z');
+               wait for 10 ns;
+
+               addr <= x"000000";
+               ce_n <= '0';
+               oe_n <= '0';
+               wait for 70 ns;
+               wait for 5 ns;
+               assert data = x"dead";
+               wait for 5 ns;
+               ce_n <= '1';
+               oe_n <= '1';
+               wait for 5 ns;
+
+               addr <= x"000001";
+               ce_n <= '0';
+               oe_n <= '0';
+               wait for 70 ns;
+               wait for 5 ns;
+               assert data = x"beef";
+               wait for 5 ns;
+               ce_n <= '1';
+               oe_n <= '1';
+               wait for 5 ns;
+
+               wait;
+       end process;
+
+       uut: entity simulated.mt45w8mw16bgx
+               port map (
+                       a     => addr(22 downto 0),
+                       dq    => data,
+                       clk   => '0',
+                       adv_n => '0',
+                       cre   => '0',
+                       ce_n  => ce_n,
+                       oe_n  => oe_n,
+                       we_n  => we_n,
+                       lb_n  => '0',
+                       ub_n  => '0',
+                       rwait => open
+               );
+
+end architecture;
diff --git a/libraries/simulated/tests/test_ps2_device.vhd b/libraries/simulated/tests/test_ps2_device.vhd
new file mode 100644 (file)
index 0000000..46418f7
--- /dev/null
@@ -0,0 +1,93 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity test_ps2_device is
+end test_ps2_device;
+
+
+architecture behavior of test_ps2_device is
+
+       signal tx_byte:  std_logic_vector(7 downto 0) := x"00";
+       signal ps2_clk:  std_logic;
+       signal ps2_data: std_logic;
+
+begin
+
+       ps2_clk <= 'H';
+       ps2_data <= 'H';
+
+       p_test: process
+       begin
+               ps2_clk <= 'Z';
+               ps2_data <= 'Z';
+
+               -- Receive bytes form device
+               wait for 100 ns;
+               tx_byte <= x"a5";
+               wait for 1 ms;
+               tx_byte <= x"a5";
+               wait for 400 us;
+               ps2_clk <= '0';  -- Cancel transmission from host
+               wait for 400 us;
+               ps2_clk <= 'Z';
+               wait for 200 us;
+               tx_byte <= x"c3";
+               wait for 1 ms;
+
+               -- Transmit byte to device
+               ps2_clk <= '0';
+               wait for 30 us;
+               ps2_data <= '0';  -- start
+               wait for 30 us;
+               ps2_clk <= 'Z';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- 0
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- 1
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- 2
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- 3
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- 4
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- 5
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- 6
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- 7
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- parity
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= '0';  -- stop
+               wait until ps2_clk /= '0';
+               wait until ps2_clk = '0';
+               ps2_data <= 'Z';  -- ack
+               assert ps2_data = '0'
+                       report "NACK from device"
+                       severity error;
+               wait until ps2_clk /= '0';
+               wait;
+       end process;
+
+       e_ps2_device: entity work.ps2_device
+               port map (
+                       fast     => true,
+
+                       tx_byte  => tx_byte,
+
+                       ps2_clk  => ps2_clk,
+                       ps2_data => ps2_data
+               );
+
+end;
diff --git a/libraries/simulated/tests/test_simmem.vhd b/libraries/simulated/tests/test_simmem.vhd
new file mode 100644 (file)
index 0000000..e00ba09
--- /dev/null
@@ -0,0 +1,70 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library work;
+
+
+entity test_simmem is
+end test_simmem;
+
+
+architecture behavior of test_simmem is
+
+       signal rst_i: std_logic;
+       signal clk_i: std_logic;
+       signal cyc_i: std_logic;
+       signal stb_i: std_logic;
+       signal we_i:  std_logic;
+       signal ack_o: std_logic;
+       signal adr_i: std_logic_vector(4 downto 0);
+       signal dat_i: std_logic_vector(7 downto 0);
+       signal dat_o: std_logic_vector(7 downto 0);
+
+begin
+
+       p_test: process
+       begin
+               -- Initial values
+               cyc_i <= '0';
+               stb_i <= '0';
+               we_i  <= '0';
+               adr_i <= (others => '0');
+               dat_i <= (others => '0');
+
+               -- Reset
+               rst_i <= '1';
+               wait for 40 ns;
+               rst_i <= '0';
+
+               -- Done
+               wait;
+       end process;
+
+
+       e_uut: entity work.wb_memory
+               generic map (
+                       INIT => x"00112233445566778899aabbccddeeff0123456789abcdeffedcba9876543210"
+               )
+               port map (
+                       rst_i => rst_i,
+                       clk_i => clk_i,
+                       cyc_i => cyc_i,
+                       stb_i => stb_i,
+                       we_i  => we_i,
+                       ack_o => ack_o,
+                       adr_i => adr_i,
+                       dat_i => dat_i,
+                       dat_o => dat_o
+               );
+
+
+       p_clk: process
+       begin
+               clk_i <= '0';
+               wait for 10 ns;
+               clk_i <= '1';
+               wait for 10 ns;
+       end process;
+
+end;
diff --git a/libraries/simulated/wb_memory.vhd b/libraries/simulated/wb_memory.vhd
new file mode 100644 (file)
index 0000000..13a8dcc
--- /dev/null
@@ -0,0 +1,49 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity wb_memory is
+       generic (
+               INIT: std_logic_vector(32*8-1 downto 0) := (others => '0')
+       );
+       port (
+               rst_i: in  std_logic;
+               clk_i: in  std_logic;
+
+               cyc_i: in  std_logic;
+               stb_i: in  std_logic;
+               we_i:  in  std_logic;
+               ack_o: out std_logic;
+               adr_i: in  std_logic_vector(4 downto 0);
+               dat_i: in  std_logic_vector(7 downto 0);
+               dat_o: out std_logic_vector(7 downto 0)
+       );
+end wb_memory;
+
+
+architecture behavioral of wb_memory is
+
+       type mem_array_t is array(natural range <>) of std_logic_vector(7 downto 0);
+
+       signal mem_array: mem_array_t(31 downto 0);
+
+begin
+
+       process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               for i in 0 to 31 loop
+                                       mem_array(31-i) <= INIT((i+1)*8-1 downto i*8);
+                               end loop;
+                       elsif cyc_i = '1' and stb_i = '1' and we_i = '1' then
+                               mem_array(to_integer(unsigned(adr_i))) <= dat_i;
+                       end if;
+               end if;
+       end process;
+
+       ack_o <= '1';
+       dat_o <= mem_array(to_integer(unsigned(adr_i)));
+
+end behavioral;
diff --git a/libraries/utility/debounce.vhd b/libraries/utility/debounce.vhd
new file mode 100644 (file)
index 0000000..375abbd
--- /dev/null
@@ -0,0 +1,39 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity debounce is
+       port (
+               clk:      in  std_logic;  -- System clock
+               clk_samp: in  std_logic;  -- Sample clock, running slower than (but in sync with) clk_i
+
+               btn_deb:  out std_logic;  -- Debounced button signal
+               btn_rise: out std_logic;  -- Pulse for one period of clk_i on rising-edge of btn_o
+               btn_fall: out std_logic;  -- Pulse for one period of clk_i on falling-edge of btn_o
+
+               btn:      in  std_logic   -- Input from bouncy button or switch
+       );
+end debounce;
+
+
+architecture behavioral of debounce is
+
+       signal edge_reg: std_logic_vector(1 downto 0);
+
+begin
+
+       process (clk, clk_samp, btn, edge_reg)
+       begin
+               if rising_edge(clk_samp) then
+                       edge_reg(0) <= btn;
+               end if;
+               if rising_edge(clk) then
+                       edge_reg(1) <= edge_reg(0);
+               end if;
+       end process;
+
+       btn_rise <= '1' when edge_reg = "01" else '0';
+       btn_fall <= '1' when edge_reg = "10" else '0';
+       btn_deb  <= edge_reg(0);
+
+end behavioral;
diff --git a/libraries/utility/fifo.vhd b/libraries/utility/fifo.vhd
new file mode 100644 (file)
index 0000000..ae2a954
--- /dev/null
@@ -0,0 +1,175 @@
+-- TODO: -- TODO: https://eecs.umich.edu/courses/doing_dsp/handout/SRL16E.pdf
+--       Play with using shift-registers to make this more dense
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+use ieee.numeric_std.all;
+
+
+entity fifo is
+       generic (
+               WIDTH: integer := 8;
+               COUNT: integer := 4
+       );
+       port (
+               -- Not a Wishbone bus, but can still use the global Wishbone SYSCON
+               rst_i:    in  std_logic;
+               clk_i:    in  std_logic;
+
+               -- Data inserted at head
+               h_stb_i:  in  std_logic;
+               h_ack_o:  out std_logic;
+               h_dat_i:  in  std_logic_vector(WIDTH-1 downto 0);
+
+               -- Data removed from tail
+               t_stb_o:  out std_logic;
+               t_ack_i:  in  std_logic;
+               t_dat_o:  out std_logic_vector(WIDTH-1 downto 0);
+
+               -- Informational signals
+               is_empty: out std_logic;
+               is_full:  out std_logic
+       );
+end fifo;
+
+
+architecture behavioral of fifo is
+
+       type data_array_t is array(natural range <>) of std_logic_vector(WIDTH-1 downto 0);
+
+       signal data_array_reg:   data_array_t(COUNT-1 downto 0);
+       signal data_array_shift: std_logic;
+
+       signal index_reg:        unsigned(4 downto 0);
+       signal index_up:         std_logic;
+       signal index_down:       std_logic;
+
+       signal full:             std_logic;
+       signal empty:            std_logic;
+
+begin
+
+       -- Wishbone-like interface
+       process (h_stb_i, t_ack_i)
+       begin
+               index_down <= '0';
+               index_up   <= '0';
+
+               if h_stb_i = '1' and full = '0' then
+                       index_up  <= '1';
+               end if;
+               if t_ack_i = '1' and empty = '0' then
+                       index_down <= '1';
+               end if;
+       end process;
+
+       t_stb_o  <= not empty;
+       h_ack_o  <= not full;
+
+       is_empty <= empty;
+       is_full  <= full;
+
+
+       -- Shift register of values
+       -- Cannot have reset or parallel-load logic if it's to be inferred as a LUT-shift-register
+       process (clk_i, index_up, data_array_reg, h_dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if index_up = '1' then
+                               for i in COUNT-1 downto 1 loop
+                                       data_array_reg(i) <= data_array_reg(i-1);
+                               end loop;
+                               data_array_reg(0) <= h_dat_i;
+                       end if;
+               end if;
+       end process;
+
+       t_dat_o <= data_array_reg(to_integer(index_reg(3 downto 0)));
+
+
+       -- Index counter
+       process (rst_i, clk_i, index_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               index_reg <= "01111";
+                       else
+                               if index_up = '1' and index_down = '0' then
+                                       index_reg <= index_reg + 1;
+                               elsif index_up = '0' and index_down = '1' then
+                                       index_reg <= index_reg - 1;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+
+       -- Emptiness/fullness detector
+       empty <= not index_reg(4);
+       full  <= and_reduce(std_logic_vector(index_reg));
+
+end behavioral;
+
+
+architecture behavioral_old of fifo is
+
+       type data_array is array(natural range <>) of std_logic_vector(WIDTH-1 downto 0);
+
+       signal stall:          std_logic_vector(COUNT-1 downto 0);
+       signal next_stall:     std_logic_vector(COUNT-1 downto 0);
+       signal valid_reg:      std_logic_vector(COUNT-1 downto 0);
+       signal prev_full:      std_logic_vector(COUNT-1 downto 0);
+       signal data_array_reg: data_array(COUNT-1 downto 0);
+       signal prev_data:      data_array(COUNT-1 downto 0);
+       signal h_ack:          std_logic;
+
+begin
+
+       -- A simple pipeline is easier to implement and smaller than a circular
+       -- buffer.  This FIFO is also intended for either storing data coming in
+       -- much slower than the clock rate (RS232, PS2, SPI, I2C) before a
+       -- processor gets around to grabbing it, or queueing up bytes from a
+       -- processor before the interface gets around to sending it - so the latency
+       -- imposed by a short (4-8 element) pipeline is insignificant.  If lower-
+       -- latency or more elements are needed the same entity can be used with a
+       -- different architecture.
+
+       -- Stall logic is a combinational chain, so depth should be kept small
+       stall      <= next_stall and valid_reg;
+       next_stall <= stall(COUNT-2 downto 0) & not t_ack_i;
+
+       -- Convenience signals
+       -- Need to check h_ack in prev_full to prevent from "accepting" new valid
+       -- bit while also refusing to acknowledge because the FIFO is full
+       prev_full  <= (h_stb_i and h_ack) & valid_reg(COUNT-1 downto 1);
+       prev_data  <= h_dat_i & data_array_reg(COUNT-1 downto 1);
+
+       -- Using !is_full for h_ack_o instead of the stall signal prevents the
+       -- combinational chain of stall signals from reaching all the way through.
+       -- This also means that a new entry cannot be added when the FIFO is full,
+       -- even if a value is being removed during the same cycle.  Probably fine.
+       h_ack      <= not and_reduce(valid_reg);
+       h_ack_o    <= h_ack;
+       t_stb_o    <= valid_reg(0);
+       t_dat_o    <= data_array_reg(0);
+       is_empty   <= not or_reduce(valid_reg);
+       is_full    <= and_reduce(valid_reg);
+
+       process (rst_i, clk_i, next_stall, prev_full, prev_data)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               valid_reg <= (others => '0');
+                               data_array_reg <= (others => (others => '0'));
+                       else
+                               for i in 0 to COUNT-1 loop
+                                       if stall(i) = '0' then
+                                               valid_reg(i)      <= prev_full(i);
+                                               data_array_reg(i) <= prev_data(i);
+                                       end if;
+                               end loop;
+                       end if;
+               end if;
+       end process;
+
+end behavioral_old;
diff --git a/libraries/utility/fifo16.vhd b/libraries/utility/fifo16.vhd
new file mode 100644 (file)
index 0000000..8eea1bb
--- /dev/null
@@ -0,0 +1,108 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+use ieee.numeric_std.all;
+
+
+entity fifo_16 is
+       generic (
+               WIDTH: integer := 8
+       );
+       port (
+               -- Not a Wishbone bus, but can still use the global Wishbone SYSCON
+               rst_i:    in  std_logic;
+               clk_i:    in  std_logic;
+
+               -- Data inserted at head
+               h_stb_i:  in  std_logic;
+               h_ack_o:  out std_logic;
+               h_dat_i:  in  std_logic_vector(WIDTH-1 downto 0);
+
+               -- Data removed from tail
+               t_stb_o:  out std_logic;
+               t_ack_i:  in  std_logic;
+               t_dat_o:  out std_logic_vector(WIDTH-1 downto 0);
+
+               -- Informational signals
+               is_empty: out std_logic;
+               is_full:  out std_logic
+       );
+end fifo_16;
+
+
+architecture behavioral of fifo_16 is
+
+       type data_array_t is array(natural range <>) of std_logic_vector(WIDTH-1 downto 0);
+
+       signal data_array_reg:   data_array_t(15 downto 0);
+       signal data_array_shift: std_logic;
+
+       signal index_reg:        unsigned(4 downto 0);
+       signal index_up:         std_logic;
+       signal index_down:       std_logic;
+
+       signal full:             std_logic;
+       signal empty:            std_logic;
+
+begin
+
+       -- Wishbone-like interface
+       process (h_stb_i, t_ack_i, full, empty)
+       begin
+               index_down <= '0';
+               index_up   <= '0';
+
+               if h_stb_i = '1' and full = '0' then
+                       index_up  <= '1';
+               end if;
+               if t_ack_i = '1' and empty = '0' then
+                       index_down <= '1';
+               end if;
+       end process;
+
+       t_stb_o  <= not empty;
+       h_ack_o  <= not full;
+
+       is_empty <= empty;
+       is_full  <= full;
+
+
+       -- Shift register of values
+       -- Cannot have reset or parallel-access logic if it's to be inferred as a LUT-shift-register
+       process (clk_i, index_up, data_array_reg, h_dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if index_up = '1' then
+                               for i in 15 downto 1 loop
+                                       data_array_reg(i) <= data_array_reg(i-1);
+                               end loop;
+                               data_array_reg(0) <= h_dat_i;
+                       end if;
+               end if;
+       end process;
+
+       t_dat_o <= data_array_reg(to_integer(index_reg(3 downto 0)));
+
+
+       -- Index counter
+       process (rst_i, clk_i, index_reg)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               index_reg <= "01111";
+                       else
+                               if index_up = '1' and index_down = '0' then
+                                       index_reg <= index_reg + 1;
+                               elsif index_up = '0' and index_down = '1' then
+                                       index_reg <= index_reg - 1;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+
+       -- Emptiness/fullness detector
+       empty <= not index_reg(4);
+       full  <= and_reduce(std_logic_vector(index_reg));
+
+end behavioral;
diff --git a/libraries/utility/perf_counter.vhd b/libraries/utility/perf_counter.vhd
new file mode 100644 (file)
index 0000000..d8b7f68
--- /dev/null
@@ -0,0 +1,199 @@
+--------------------------------------------------------------------------------
+-- Set of four 56-bit performance counters
+--
+-- Each counter can be independantly configured to count:
+-- - Rising edges
+-- - Falling edges
+-- - Clock cycles during which a signal is high
+-- - Clock cycles during which a signal is low
+--
+--            +---+---+---+---+---+---+---+---+
+--            | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+--            +---+---+---+---+---+---+---+---+
+--       0x00 |               |C3 |C2 |C1 |C0 | HALT
+--            +---------------+---+---+---+---+
+--       0x01 |               |C3 |C2 |C1 |C0 | CLEAR
+--            +---------------+---+---+---+---+
+--       0x02 |               |C3 |C2 |C1 |C0 | EDGE
+--            +---------------+---+---+---+---+
+--       0x03 |               |C3 |C2 |C1 |C0 | PLRTY
+--            +---------------+---+---+---+---+
+--  0x04-0x0A |          CTR0 (L.E.)          |
+--            +-------------------------------+
+--  0x0B-0x11 |          CTR1 (L.E.)          |
+--            +-------------------------------+
+--  0x12-0x18 |          CTR2 (L.E.)          |
+--            +-------------------------------+
+--  0x19-0x1f |          CTR3 (L.E.)          |
+--            +-------------------------------+
+--
+-- HALT  - Set bits inhibit the corresponding counter from counting
+-- CLEAR - Writing one clears the corresponding counter
+-- EDGE  - Set bits configure edge counting rather than cycles-at-level counting
+-- PLRTY - Set bits configure counters to count high-level or rising-edges
+--------------------------------------------------------------------------------
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity perf_counter is
+       port (
+               rst_i: in  std_logic;
+               clk_i: in  std_logic;
+
+               cyc_i: in  std_logic;
+               stb_i: in  std_logic;
+               we_i:  in  std_logic;
+               ack_o: out std_logic;
+               adr_i: in  std_logic_vector(4 downto 0);
+               dat_i: in  std_logic_vector(7 downto 0);
+               dat_o: out std_logic_vector(7 downto 0);
+
+               trig:  in  std_logic_vector(3 downto 0)
+       );
+end perf_counter;
+
+
+architecture behavioral of perf_counter is
+
+       type count_array_t is array(natural range <>) of std_logic_vector(55 downto 0);
+
+       -- Edge detection
+       signal prev_reg: std_logic_vector(3 downto 0);
+       signal rising:   std_logic_vector(3 downto 0);
+       signal falling:  std_logic_vector(3 downto 0);
+
+       -- Wishbone-visible registers
+       signal halt:     std_logic_vector(3 downto 0);
+       signal edge:     std_logic_vector(3 downto 0);
+       signal plrty:    std_logic_vector(3 downto 0);
+       signal counters: count_array_t(3 downto 0);
+
+       -- Control signals
+       signal e_trig:   std_logic_vector(3 downto 0);
+       signal l_trig:   std_logic_vector(3 downto 0);
+       signal trigger:  std_logic_vector(3 downto 0);
+       signal load_en:  std_logic_vector(3 downto 0);
+       signal count_en: std_logic_vector(3 downto 0);
+       signal clear_en: std_logic_vector(3 downto 0);
+
+begin
+
+       -- Wishbone bus
+       ack_o <= '1';
+
+       with adr_i select dat_o <=
+               "0000" & halt             when "00000",
+               (others => '0')           when "00001",
+               "0000" & edge             when "00010",
+               "0000" & plrty            when "00011",
+               counters(0)( 7 downto  0) when "00100",
+               counters(0)(15 downto  8) when "00101",
+               counters(0)(23 downto 16) when "00110",
+               counters(0)(31 downto 24) when "00111",
+               counters(0)(39 downto 32) when "01000",
+               counters(0)(47 downto 40) when "01001",
+               counters(0)(55 downto 48) when "01010",
+               counters(1)( 7 downto  0) when "01011",
+               counters(1)(15 downto  8) when "01100",
+               counters(1)(23 downto 16) when "01101",
+               counters(1)(31 downto 24) when "01110",
+               counters(1)(39 downto 32) when "01111",
+               counters(1)(47 downto 40) when "10000",
+               counters(1)(55 downto 48) when "10001",
+               counters(2)( 7 downto  0) when "10010",
+               counters(2)(15 downto  8) when "10011",
+               counters(2)(23 downto 16) when "10100",
+               counters(2)(31 downto 24) when "10101",
+               counters(2)(39 downto 32) when "10110",
+               counters(2)(47 downto 40) when "10111",
+               counters(2)(55 downto 48) when "11000",
+               counters(3)( 7 downto  0) when "11001",
+               counters(3)(15 downto  8) when "11010",
+               counters(3)(23 downto 16) when "11011",
+               counters(3)(31 downto 24) when "11100",
+               counters(3)(39 downto 32) when "11101",
+               counters(3)(47 downto 40) when "11110",
+               counters(3)(55 downto 48) when "11111",
+               (others => '1') when others;
+
+       -- Edge detection
+       process (clk_i)
+       begin
+               if rising_edge(clk_i) then
+                       prev_reg <= trig;
+               end if;
+       end process;
+       rising  <= (not prev_reg) and trig;
+       falling <= prev_reg and (not trig);
+
+       -- Count enable signals
+       e_trig   <= (rising and plrty) or (falling and (not plrty));  -- Select rising or falling edge based on polarity reg
+       l_trig   <= trig xor (not plrty);                             -- Invert level if low polarity is selected
+       trigger  <= (e_trig and edge) or (l_trig and (not edge));     -- Select edge or level triggers based on edge reg
+       count_en <= trigger and (not halt);                           -- Mask trigger signal when halted
+
+       -- Counter logic
+       process (rst_i, clk_i,
+                clear_en, count_en, counters,
+                cyc_i, stb_i, we_i, adr_i, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               for i in 0 to 3 loop
+                                       counters(i) <= (others => '0');
+                               end loop;
+                       else
+                               -- Wishbone writes need to be in here to have counter registers written by one process
+                               if cyc_i = '1' and stb_i = '1' and we_i = '1' then
+                                       case adr_i is
+                                               when "00000" => halt                      <= dat_i(3 downto 0);
+                                               when "00001" => clear_en                  <= dat_i(3 downto 0);
+                                               when "00010" => edge                      <= dat_i(3 downto 0);
+                                               when "00011" => plrty                     <= dat_i(3 downto 0);
+                                               when "00100" => counters(0)( 7 downto  0) <= dat_i;
+                                               when "00101" => counters(0)(15 downto  8) <= dat_i;
+                                               when "00110" => counters(0)(23 downto 16) <= dat_i;
+                                               when "00111" => counters(0)(31 downto 24) <= dat_i;
+                                               when "01000" => counters(0)(39 downto 32) <= dat_i;
+                                               when "01001" => counters(0)(47 downto 40) <= dat_i;
+                                               when "01010" => counters(0)(55 downto 48) <= dat_i;
+                                               when "01011" => counters(1)( 7 downto  0) <= dat_i;
+                                               when "01100" => counters(1)(15 downto  8) <= dat_i;
+                                               when "01101" => counters(1)(23 downto 16) <= dat_i;
+                                               when "01110" => counters(1)(31 downto 24) <= dat_i;
+                                               when "01111" => counters(1)(39 downto 32) <= dat_i;
+                                               when "10000" => counters(1)(47 downto 40) <= dat_i;
+                                               when "10001" => counters(1)(55 downto 48) <= dat_i;
+                                               when "10010" => counters(2)( 7 downto  0) <= dat_i;
+                                               when "10011" => counters(2)(15 downto  8) <= dat_i;
+                                               when "10100" => counters(2)(23 downto 16) <= dat_i;
+                                               when "10101" => counters(2)(31 downto 24) <= dat_i;
+                                               when "10110" => counters(2)(39 downto 32) <= dat_i;
+                                               when "10111" => counters(2)(47 downto 40) <= dat_i;
+                                               when "11000" => counters(2)(55 downto 48) <= dat_i;
+                                               when "11001" => counters(3)( 7 downto  0) <= dat_i;
+                                               when "11010" => counters(3)(15 downto  8) <= dat_i;
+                                               when "11011" => counters(3)(23 downto 16) <= dat_i;
+                                               when "11100" => counters(3)(31 downto 24) <= dat_i;
+                                               when "11101" => counters(3)(39 downto 32) <= dat_i;
+                                               when "11110" => counters(3)(47 downto 40) <= dat_i;
+                                               when "11111" => counters(3)(55 downto 48) <= dat_i;
+                                               when others  => null;
+                                       end case;
+                               end if;
+
+                               -- Count and clear logic
+                               for i in 0 to 3 loop
+                                       if clear_en(i) = '1' then
+                                               counters(i) <= (others => '0');
+                                       elsif count_en(i) = '1' then
+                                               counters(i) <= std_logic_vector(unsigned(counters(i)) + 1);
+                                       end if;
+                               end loop;
+                       end if;
+               end if;
+       end process;
+
+end behavioral;
diff --git a/libraries/utility/power_on_reset.vhd b/libraries/utility/power_on_reset.vhd
new file mode 100644 (file)
index 0000000..f533533
--- /dev/null
@@ -0,0 +1,31 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity power_on_reset is
+       port (
+               rst_i: in  std_logic;
+               clk_i: in  std_logic;
+               rst_o: out std_logic
+       );
+end power_on_reset;
+
+
+architecture behavioral of power_on_reset is
+
+       signal shift_reg: std_logic_vector(7 downto 0) := (others => '1');
+
+begin
+
+       process (rst_i, clk_i, shift_reg)
+       begin
+               if rst_i = '1' then
+                       shift_reg <= (others => '1');
+               elsif rising_edge(clk_i) then
+                       shift_reg <= '0' & shift_reg(shift_reg'high downto 1);
+               end if;
+       end process;
+
+       rst_o <= shift_reg(0);
+
+end behavioral;
diff --git a/libraries/utility/tests/test_fifo.vhd b/libraries/utility/tests/test_fifo.vhd
new file mode 100644 (file)
index 0000000..54bfa9e
--- /dev/null
@@ -0,0 +1,169 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library utility;
+library work;
+
+entity test_fifo is
+end test_fifo;
+
+
+architecture behavior of test_fifo is
+
+       constant clk_i_period : time := 20 ns;
+
+       signal rst:      std_logic := '0';
+       signal clk:      std_logic := '0';
+
+       signal h_ack:    std_logic;
+       signal h_stb:    std_logic := '0';
+       signal h_dat:    std_logic_vector(7 downto 0) := (others => '0');
+
+       signal t_stb:    std_logic;
+       signal t_ack:    std_logic := '0';
+       signal t_dat:    std_logic_vector(7 downto 0);
+
+       signal is_empty: std_logic;
+       signal is_full:  std_logic;
+
+begin
+
+       p_test: process
+       begin
+               h_stb <= '0';
+               t_ack <= '0';
+               h_dat <= x"00";
+
+               -- Reset
+               rst <= '1';
+               wait for clk_i_period * 2;
+               rst <= '0';
+
+               -- Initial state
+               assert h_ack    = '1' report "h_ack";
+               assert t_stb    = '0' report "t_stb";
+               assert is_empty = '1' report "is_empty";
+               assert is_full  = '0' report "is_full";
+
+               -- Insert a value
+               h_dat <= x"a5";
+               h_stb <= '1';
+               wait for clk_i_period;
+               h_stb <= '0';
+
+               -- t_stb will still be '0' for the pipeline implementation, but don't
+               -- test that here so the test will still work for circular-buffer
+               -- implementations later, where tail_ready may be '1' immediately.
+               assert h_ack    = '1' report "h_ack";
+               assert is_empty = '0' report "is_empty";
+               assert is_full  = '0' report "is_full";
+
+               if t_stb = '0' then
+                       wait until t_stb = '1';
+               end if;
+               wait for clk_i_period * 3;
+               assert t_dat = x"a5" report "t_dat";
+
+               -- Remove the value
+               t_ack <= '1';
+               wait for clk_i_period;
+               t_ack <= '0';
+
+               wait for clk_i_period * 2;
+
+               -- Insert two values
+               h_dat <= x"5a";
+               h_stb <= '1';
+               wait for clk_i_period;
+               h_dat <= x"c3";
+               wait for clk_i_period;
+               h_stb <= '0';
+
+               -- Remove one value
+               if t_stb = '0' then
+                       wait until t_stb = '1';
+               end if;
+               t_ack <= '1';
+               wait for clk_i_period;
+               t_ack <= '0';
+               wait for clk_i_period;
+
+               -- Fill the queue
+               h_dat <= x"11";
+               h_stb <= '1';
+               wait for clk_i_period;
+               h_dat <= x"22";
+               wait for clk_i_period;
+               h_dat <= x"33";
+               wait for clk_i_period;
+               h_dat <= x"44";
+               wait for clk_i_period;
+               h_dat <= x"55";
+               wait for clk_i_period;
+               h_dat <= x"55";
+               wait for clk_i_period;
+               h_dat <= x"66";
+               wait for clk_i_period;
+               h_dat <= x"77";
+               wait for clk_i_period;
+               h_dat <= x"88";
+               wait for clk_i_period;
+               h_dat <= x"99";
+               wait for clk_i_period;
+               h_dat <= x"aa";
+               wait for clk_i_period;
+               h_dat <= x"bb";
+               wait for clk_i_period;
+               h_dat <= x"cc";
+               wait for clk_i_period;
+               h_dat <= x"dd";
+               wait for clk_i_period;
+               h_dat <= x"ee";
+               wait for clk_i_period;
+               --h_stb <= '0';
+               --wait for 200 ns;
+               --h_stb <= '1';
+               h_dat <= x"ff";
+               wait for clk_i_period;
+               h_stb <= '0';
+
+               wait for clk_i_period;
+
+               -- Remove one value
+               t_ack <= '1';
+               wait for clk_i_period;
+               t_ack <= '0';
+
+               wait;
+       end process;
+
+       e_uut: entity utility.fifo_16
+               generic map (
+                       WIDTH    => 8
+               )
+               port map (
+                       rst_i    => rst,
+                       clk_i    => clk,
+
+                       h_stb_i  => h_stb,
+                       h_ack_o  => h_ack,
+                       h_dat_i  => h_dat,
+
+                       t_stb_o  => t_stb,
+                       t_ack_i  => t_ack,
+                       t_dat_o  => t_dat,
+
+                       is_empty => is_empty,
+                       is_full  => is_full
+               );
+
+       p_clk: process
+       begin
+               clk <= '0';
+               wait for clk_i_period/2;
+               clk <= '1';
+               wait for clk_i_period/2;
+       end process;
+
+end architecture;
diff --git a/libraries/utility/tests/test_mapper.vhd b/libraries/utility/tests/test_mapper.vhd
new file mode 100644 (file)
index 0000000..5b033fc
--- /dev/null
@@ -0,0 +1,202 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library work;
+
+
+entity test_mapper is
+end test_mapper;
+
+
+architecture behavior of test_mapper is
+
+       signal cyc:       std_logic;
+       signal stb:       std_logic;
+       signal we:        std_logic;
+       signal ack:       std_logic;
+       signal adr:       std_logic_vector(31 downto 0);
+       signal dat_mosi:  std_logic_vector(7 downto 0);
+       signal dat_miso:  std_logic_vector(7 downto 0);
+
+       signal mem_cyc:   std_logic;
+       signal mem_stb:   std_logic;
+       signal mem_we:    std_logic;
+       signal mem_ack:   std_logic;
+       signal mem_adr:   std_logic_vector(31 downto 0);
+       signal mem_mosi:  std_logic_vector(7 downto 0);
+       signal mem_miso:  std_logic_vector(7 downto 0);
+
+       signal tile_cyc:  std_logic;
+       signal tile_stb:  std_logic;
+       signal tile_we:   std_logic;
+       signal tile_ack:  std_logic;
+       signal tile_adr:  std_logic_vector(31 downto 0);
+       signal tile_mosi: std_logic_vector(7 downto 0);
+       signal tile_miso: std_logic_vector(7 downto 0);
+
+       signal host_cyc:  std_logic;
+       signal host_stb:  std_logic;
+       signal host_we:   std_logic;
+       signal host_ack:  std_logic;
+       signal host_adr:  std_logic_vector(31 downto 0);
+       signal host_mosi: std_logic_vector(7 downto 0);
+       signal host_miso: std_logic_vector(7 downto 0);
+
+       signal ps2_cyc:   std_logic;
+       signal ps2_stb:   std_logic;
+       signal ps2_we:    std_logic;
+       signal ps2_ack:   std_logic;
+       signal ps2_adr:   std_logic_vector(31 downto 0);
+       signal ps2_mosi:  std_logic_vector(7 downto 0);
+       signal ps2_miso:  std_logic_vector(7 downto 0);
+
+       signal uart_cyc:  std_logic;
+       signal uart_stb:  std_logic;
+       signal uart_we:   std_logic;
+       signal uart_ack:  std_logic;
+       signal uart_adr:  std_logic_vector(31 downto 0);
+       signal uart_mosi: std_logic_vector(7 downto 0);
+       signal uart_miso: std_logic_vector(7 downto 0);
+
+begin
+
+       p_test: process
+       begin
+               -- Initial values
+               cyc       <= '0';
+               stb       <= '0';
+               we        <= '0';
+               adr       <= x"00000000";
+               dat_mosi  <= x"00";
+               mem_miso  <= x"11";
+               tile_miso <= x"aa";
+               host_miso <= x"bb";
+               ps2_miso  <= x"cc";
+               uart_miso <= x"dd";
+               mem_ack   <= '0';
+               tile_ack  <= '0';
+               host_ack  <= '0';
+               ps2_ack   <= '0';
+               uart_ack  <= '0';
+
+               cyc      <= '1';
+               stb      <= '1';
+               we       <= '0';
+               adr      <= x"00000000";
+               dat_mosi <= x"00";
+
+               wait for 100 ns;
+
+               cyc      <= '1';
+               stb      <= '1';
+               we       <= '0';
+               adr      <= x"02000000";
+               dat_mosi <= x"00";
+
+               -- Done
+               wait;
+       end process;
+
+       e_uut: entity work.wb_mapper
+               generic map (
+                       A_WIDTH => 32,
+                       D_WIDTH => 8
+               )
+               port map (
+                       cyc_i   => cyc,
+                       stb_i   => stb,
+                       we_i    => we,
+                       ack_o   => ack,
+                       adr_i   => adr,
+                       dat_i   => dat_mosi,
+                       dat_o   => dat_miso,
+
+                       -- Flash: 0x00000000-0x00ffffff
+                       -- Ram:   0x01000000-0x01ffffff
+                       mask_0  => "00000010000000000000000000000000",
+                       match_0 => "00000000000000000000000000000000",
+                       cyc_o_0 => mem_cyc,
+                       stb_o_0 => mem_stb,
+                       we_o_0  => mem_we,
+                       ack_i_0 => mem_ack,
+                       adr_o_0 => mem_adr,
+                       dat_o_0 => mem_mosi,
+                       dat_i_0 => mem_miso,
+
+                       -- Vbuf:  0x02000000-0x02001fff
+                       -- Tiles: 0x02002000-0x02003fff
+                       mask_1  => "00000010000000000100000000000000",
+                       match_1 => "00000010000000000000000000000000",
+                       cyc_o_1 => tile_cyc,
+                       stb_o_1 => tile_stb,
+                       we_o_1  => tile_we,
+                       ack_i_1 => tile_ack,
+                       adr_o_1 => tile_adr,
+                       dat_o_1 => tile_mosi,
+                       dat_i_1 => tile_miso,
+
+                       -- Host:  0x02004000-0x02004007
+                       mask_2  => "00000010000000000100000000011000",
+                       match_2 => "00000010000000000100000000000000",
+                       cyc_o_2 => host_cyc,
+                       stb_o_2 => host_stb,
+                       we_o_2  => host_we,
+                       ack_i_2 => host_ack,
+                       adr_o_2 => host_adr,
+                       dat_o_2 => host_mosi,
+                       dat_i_2 => host_miso,
+
+                       -- PS2:   0x02004008-0x0200400f
+                       mask_3  => "00000010000000000100000000011000",
+                       match_3 => "00000010000000000100000000001000",
+                       cyc_o_3 => ps2_cyc,
+                       stb_o_3 => ps2_stb,
+                       we_o_3  => ps2_we,
+                       ack_i_3 => ps2_ack,
+                       adr_o_3 => ps2_adr,
+                       dat_o_3 => ps2_mosi,
+                       dat_i_3 => ps2_miso,
+
+                       -- UART:  0x02004010-0x02004017
+                       mask_4  => "00000010000000000100000000011000",
+                       match_4 => "00000010000000000100000000010000",
+                       cyc_o_4 => uart_cyc,
+                       stb_o_4 => uart_stb,
+                       we_o_4  => uart_we,
+                       ack_i_4 => uart_ack,
+                       adr_o_4 => uart_adr,
+                       dat_o_4 => uart_mosi,
+                       dat_i_4 => uart_miso,
+
+                       mask_5  => "00000000000000000000000000000000",
+                       match_5 => "00000000000000000000000000000001",
+                       cyc_o_5 => open,
+                       stb_o_5 => open,
+                       we_o_5  => open,
+                       ack_i_5 => '1',
+                       adr_o_5 => open,
+                       dat_o_5 => open,
+                       dat_i_5 => "00000000",
+
+                       mask_6  => "00000000000000000000000000000000",
+                       match_6 => "00000000000000000000000000000001",
+                       cyc_o_6 => open,
+                       stb_o_6 => open,
+                       we_o_6  => open,
+                       ack_i_6 => '1',
+                       adr_o_6 => open,
+                       dat_o_6 => open,
+                       dat_i_6 => "00000000",
+
+                       mask_7  => "00000000000000000000000000000000",
+                       match_7 => "00000000000000000000000000000001",
+                       cyc_o_7 => open,
+                       stb_o_7 => open,
+                       we_o_7  => open,
+                       ack_i_7 => '1',
+                       adr_o_7 => open,
+                       dat_o_7 => open,
+                       dat_i_7 => "00000000"
+               );
+
+end;
diff --git a/libraries/utility/wb_debug.vhd b/libraries/utility/wb_debug.vhd
new file mode 100644 (file)
index 0000000..32bedae
--- /dev/null
@@ -0,0 +1,64 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity wb_debug is
+       port (
+               rst_i:   in  std_logic;
+               clk_i:   in  std_logic;
+
+               cyc_i:   in  std_logic;
+               stb_i:   in  std_logic;
+               we_i:    in  std_logic;
+               ack_o:   out std_logic;
+               adr_i:   in  std_logic_vector(2 downto 0);
+               dat_i:   in  std_logic_vector(7 downto 0);
+               dat_o:   out std_logic_vector(7 downto 0);
+
+               debug_i: in  std_logic_vector(63 downto 0);
+               debug_o: out std_logic_vector(63 downto 0)
+       );
+end wb_debug;
+
+
+architecture behavioral of wb_debug is
+
+       signal debug_reg: std_logic_vector(63 downto 0);
+
+begin
+
+       process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               debug_reg <= (others => '0');
+                       elsif cyc_i = '1' and stb_i = '1' and we_i = '1' then
+                               case adr_i is
+                                       when "000"  => debug_reg( 7 downto  0) <= dat_i;
+                                       when "001"  => debug_reg(15 downto  8) <= dat_i;
+                                       when "010"  => debug_reg(23 downto 16) <= dat_i;
+                                       when "011"  => debug_reg(31 downto 24) <= dat_i;
+                                       when "100"  => debug_reg(39 downto 32) <= dat_i;
+                                       when "101"  => debug_reg(47 downto 40) <= dat_i;
+                                       when "110"  => debug_reg(55 downto 48) <= dat_i;
+                                       when others => debug_reg(63 downto 56) <= dat_i;
+                               end case;
+                       end if;
+               end if;
+       end process;
+
+       with adr_i select dat_o <=
+               debug_i( 7 downto  0) when "000",
+               debug_i(15 downto  8) when "001",
+               debug_i(23 downto 16) when "010",
+               debug_i(31 downto 24) when "011",
+               debug_i(39 downto 32) when "100",
+               debug_i(47 downto 40) when "101",
+               debug_i(55 downto 48) when "110",
+               debug_i(63 downto 56) when others;
+
+       ack_o <= '1';
+
+       debug_o <= debug_reg;
+
+end behavioral;
diff --git a/libraries/utility/wb_mapper.vhd b/libraries/utility/wb_mapper.vhd
new file mode 100644 (file)
index 0000000..87f68ff
--- /dev/null
@@ -0,0 +1,212 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity wb_mapper is
+       generic (
+               A_WIDTH: integer := 32;
+               D_WIDTH: integer := 8
+       );
+       port (
+               -- Master Wishbone interface
+               cyc_i:   in  std_logic;
+               stb_i:   in  std_logic;
+               we_i:    in  std_logic;
+               ack_o:   out std_logic;
+               adr_i:   in  std_logic_vector(31 downto 0);
+               dat_i:   in  std_logic_vector(7 downto 0);
+               dat_o:   out std_logic_vector(7 downto 0);
+
+               -- Device wishbone interfaces
+               mask_0:  in  std_logic_vector(A_WIDTH-1 downto 0);
+               match_0: in  std_logic_vector(A_WIDTH-1 downto 0);
+               cyc_o_0: out std_logic;
+               stb_o_0: out std_logic;
+               we_o_0:  out std_logic;
+               ack_i_0: in  std_logic;
+               adr_o_0: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_0: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_0: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               mask_1:  in  std_logic_vector(A_WIDTH-1 downto 0);
+               match_1: in  std_logic_vector(A_WIDTH-1 downto 0);
+               cyc_o_1: out std_logic;
+               stb_o_1: out std_logic;
+               we_o_1:  out std_logic;
+               ack_i_1: in  std_logic;
+               adr_o_1: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_1: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_1: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               mask_2:  in  std_logic_vector(A_WIDTH-1 downto 0);
+               match_2: in  std_logic_vector(A_WIDTH-1 downto 0);
+               cyc_o_2: out std_logic;
+               stb_o_2: out std_logic;
+               we_o_2:  out std_logic;
+               ack_i_2: in  std_logic;
+               adr_o_2: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_2: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_2: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               mask_3:  in  std_logic_vector(A_WIDTH-1 downto 0);
+               match_3: in  std_logic_vector(A_WIDTH-1 downto 0);
+               cyc_o_3: out std_logic;
+               stb_o_3: out std_logic;
+               we_o_3:  out std_logic;
+               ack_i_3: in  std_logic;
+               adr_o_3: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_3: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_3: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               mask_4:  in  std_logic_vector(A_WIDTH-1 downto 0);
+               match_4: in  std_logic_vector(A_WIDTH-1 downto 0);
+               cyc_o_4: out std_logic;
+               stb_o_4: out std_logic;
+               we_o_4:  out std_logic;
+               ack_i_4: in  std_logic;
+               adr_o_4: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_4: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_4: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               mask_5:  in  std_logic_vector(A_WIDTH-1 downto 0);
+               match_5: in  std_logic_vector(A_WIDTH-1 downto 0);
+               cyc_o_5: out std_logic;
+               stb_o_5: out std_logic;
+               we_o_5:  out std_logic;
+               ack_i_5: in  std_logic;
+               adr_o_5: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_5: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_5: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               mask_6:  in  std_logic_vector(A_WIDTH-1 downto 0);
+               match_6: in  std_logic_vector(A_WIDTH-1 downto 0);
+               cyc_o_6: out std_logic;
+               stb_o_6: out std_logic;
+               we_o_6:  out std_logic;
+               ack_i_6: in  std_logic;
+               adr_o_6: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_6: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_6: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               mask_7:  in  std_logic_vector(A_WIDTH-1 downto 0);
+               match_7: in  std_logic_vector(A_WIDTH-1 downto 0);
+               cyc_o_7: out std_logic;
+               stb_o_7: out std_logic;
+               we_o_7:  out std_logic;
+               ack_i_7: in  std_logic;
+               adr_o_7: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_7: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_7: in  std_logic_vector(D_WIDTH-1 downto 0)
+       );
+end wb_mapper;
+
+
+architecture behavioral of wb_mapper is
+
+       signal sel_0: std_logic;
+       signal sel_1: std_logic;
+       signal sel_2: std_logic;
+       signal sel_3: std_logic;
+       signal sel_4: std_logic;
+       signal sel_5: std_logic;
+       signal sel_6: std_logic;
+       signal sel_7: std_logic;
+       signal dummy: std_logic;
+
+       signal en_0:  std_logic_vector(7 downto 0);
+       signal en_1:  std_logic_vector(7 downto 0);
+       signal en_2:  std_logic_vector(7 downto 0);
+       signal en_3:  std_logic_vector(7 downto 0);
+       signal en_4:  std_logic_vector(7 downto 0);
+       signal en_5:  std_logic_vector(7 downto 0);
+       signal en_6:  std_logic_vector(7 downto 0);
+       signal en_7:  std_logic_vector(7 downto 0);
+
+begin
+
+       sel_0   <= '1' when (adr_i and mask_0) = match_0 else '0';
+       en_0    <= (others => sel_0);
+       cyc_o_0 <= cyc_i and sel_0;
+       stb_o_0 <= stb_i;
+       we_o_0  <= we_i;
+       adr_o_0 <= adr_i;
+       dat_o_0 <= dat_i;
+
+       sel_1   <= '1' when (adr_i and mask_1) = match_1 else '0';
+       en_1    <= (others => sel_1);
+       cyc_o_1 <= cyc_i and sel_1;
+       stb_o_1 <= stb_i;
+       we_o_1  <= we_i;
+       adr_o_1 <= adr_i;
+       dat_o_1 <= dat_i;
+
+       sel_2   <= '1' when (adr_i and mask_2) = match_2 else '0';
+       en_2    <= (others => sel_2);
+       cyc_o_2 <= cyc_i and sel_2;
+       stb_o_2 <= stb_i;
+       we_o_2  <= we_i;
+       adr_o_2 <= adr_i;
+       dat_o_2 <= dat_i;
+
+       sel_3   <= '1' when (adr_i and mask_3) = match_3 else '0';
+       en_3    <= (others => sel_3);
+       cyc_o_3 <= cyc_i and sel_3;
+       stb_o_3 <= stb_i;
+       we_o_3  <= we_i;
+       adr_o_3 <= adr_i;
+       dat_o_3 <= dat_i;
+
+       sel_4   <= '1' when (adr_i and mask_4) = match_4 else '0';
+       en_4    <= (others => sel_4);
+       cyc_o_4 <= cyc_i and sel_4;
+       stb_o_4 <= stb_i;
+       we_o_4  <= we_i;
+       adr_o_4 <= adr_i;
+       dat_o_4 <= dat_i;
+
+       sel_5   <= '1' when (adr_i and mask_5) = match_5 else '0';
+       en_5    <= (others => sel_5);
+       cyc_o_5 <= cyc_i and sel_5;
+       stb_o_5 <= stb_i;
+       we_o_5  <= we_i;
+       adr_o_5 <= adr_i;
+       dat_o_5 <= dat_i;
+
+       sel_6   <= '1' when (adr_i and mask_6) = match_6 else '0';
+       en_6    <= (others => sel_6);
+       cyc_o_6 <= cyc_i and sel_6;
+       stb_o_6 <= stb_i;
+       we_o_6  <= we_i;
+       adr_o_6 <= adr_i;
+       dat_o_6 <= dat_i;
+
+       sel_7   <= '1' when (adr_i and mask_7) = match_7 else '0';
+       en_7    <= (others => sel_7);
+       cyc_o_7 <= cyc_i and sel_7;
+       stb_o_7 <= stb_i;
+       we_o_7  <= we_i;
+       adr_o_7 <= adr_i;
+       dat_o_7 <= dat_i;
+
+       dummy <= not (sel_0 or sel_1 or sel_2 or sel_3 or sel_4 or sel_5 or sel_6 or sel_7);
+
+       ack_o <= (ack_i_0 and sel_0) or
+                (ack_i_1 and sel_1) or
+                (ack_i_2 and sel_2) or
+                (ack_i_3 and sel_3) or
+                (ack_i_4 and sel_4) or
+                (ack_i_5 and sel_5) or
+                (ack_i_6 and sel_6) or
+                (ack_i_7 and sel_7) or
+                dummy;  -- If none are selected, acknowledge to eat the bus transaction
+
+       dat_o <= (dat_i_0 and en_0) or
+                (dat_i_1 and en_1) or
+                (dat_i_2 and en_2) or
+                (dat_i_3 and en_3) or
+                (dat_i_4 and en_4) or
+                (dat_i_5 and en_5) or
+                (dat_i_6 and en_6) or
+                (dat_i_7 and en_7);
+
+end behavioral;
diff --git a/libraries/utility/wb_mux2.vhd b/libraries/utility/wb_mux2.vhd
new file mode 100644 (file)
index 0000000..e0eb31d
--- /dev/null
@@ -0,0 +1,43 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity wb_mux2 is
+       generic (
+               WIDTH: integer := 8
+       );
+       port (
+               sel:     in  std_logic;
+
+               -- Single master
+               cyc_i:   in  std_logic;
+               ack_o:   out std_logic;
+               dat_o:   out std_logic_vector(WIDTH-1 downto 0);
+
+               -- Multiple devices
+               cyc_o_0: out std_logic;
+               ack_i_0: in  std_logic;
+               dat_i_0: in  std_logic_vector(WIDTH-1 downto 0);
+
+               cyc_o_1: out std_logic;
+               ack_i_1: in  std_logic;
+               dat_i_1: in  std_logic_vector(WIDTH-1 downto 0)
+       );
+end wb_mux2;
+
+
+architecture behavioral of wb_mux2 is
+begin
+
+       cyc_o_0 <= cyc_i when sel  = '0' else '0';
+       cyc_o_1 <= cyc_i when sel /= '0' else '0';
+
+       with sel select dat_o <=
+               dat_i_0 when '0',
+               dat_i_1 when others;
+
+       with sel select ack_o <=
+               ack_i_0 when '0',
+               ack_i_1 when others;
+
+end behavioral;
diff --git a/libraries/utility/wb_mux4.vhd b/libraries/utility/wb_mux4.vhd
new file mode 100644 (file)
index 0000000..53bf871
--- /dev/null
@@ -0,0 +1,97 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity wb_mux4 is
+       generic (
+               A_WIDTH: integer := 32;
+               D_WIDTH: integer := 8
+       );
+       port (
+               sel:   in  std_logic_vector(1 downto 0);
+
+               -- Wishbone interface to master
+               cyc_i: in  std_logic;
+               stb_i: in  std_logic;
+               we_i:  in  std_logic;
+               ack_o: out std_logic;
+               adr_i: in  std_logic_vector(A_WIDTH-1 downto 0);
+               dat_i: in  std_logic_vector(D_WIDTH-1 downto 0);
+               dat_o: out std_logic_vector(D_WIDTH-1 downto 0);
+
+               -- Wishbone interfaces to devices
+               cyc_o_0: out std_logic;
+               stb_o_0: out std_logic;
+               we_o_0:  in  std_logic;
+               ack_i_0: out std_logic;
+               adr_o_0: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_0: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_0: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               cyc_o_1: out std_logic;
+               stb_o_1: out std_logic;
+               we_o_1:  in  std_logic;
+               ack_i_1: out std_logic;
+               adr_o_1: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_1: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_1: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               cyc_o_2: out std_logic;
+               stb_o_2: out std_logic;
+               we_o_2:  in  std_logic;
+               ack_i_2: out std_logic;
+               adr_o_2: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_2: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_2: in  std_logic_vector(D_WIDTH-1 downto 0);
+
+               cyc_o_3: out std_logic;
+               stb_o_3: out std_logic;
+               we_o_3:  in  std_logic;
+               ack_i_3: out std_logic;
+               adr_o_3: out std_logic_vector(A_WIDTH-1 downto 0);
+               dat_o_3: out std_logic_vector(D_WIDTH-1 downto 0);
+               dat_i_3: in  std_logic_vector(D_WIDTH-1 downto 0)
+       );
+end wb_mux4;
+
+
+architecture behavioral of wb_mux4 is
+begin
+
+       cyc_o_0 <= cyc_i when sel = "00" else '0';
+       stb_o_0 <= stb_i;
+       we_o_0  <= we_i;
+       adr_o_0 <= adr_i;
+       dat_o_0 <= dat_i;
+
+       cyc_o_1 <= cyc_i when sel = "01" else '0';
+       stb_o_1 <= stb_i;
+       we_o_1  <= we_i;
+       adr_o_1 <= adr_i;
+       dat_o_1 <= dat_i;
+
+       cyc_o_2 <= cyc_i when sel = "10" else '0';
+       stb_o_2 <= stb_i;
+       we_o_2  <= we_i;
+       adr_o_2 <= adr_i;
+       dat_o_2 <= dat_i;
+
+       cyc_o_3 <= cyc_i when sel = "11" else '0';
+       stb_o_3 <= stb_i;
+       we_o_3  <= we_i;
+       adr_o_3 <= adr_i;
+       dat_o_3 <= dat_i;
+
+       with sel select ack_o <=
+               ack_i_0 when "00",
+               ack_i_1 when "01",
+               ack_i_2 when "10",
+               ack_i_3 when others;
+
+       with sel select dat_o <=
+               dat_i_0 when "00",
+               dat_i_1 when "01",
+               dat_i_2 when "10",
+               dat_i_3 when others;
+
+end behavioral;
diff --git a/libraries/vga/tests/program.sh b/libraries/vga/tests/program.sh
new file mode 100755 (executable)
index 0000000..e2092f0
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f test_nexys2.bit prog
+
diff --git a/libraries/vga/tests/test_nexys2.vhd b/libraries/vga/tests/test_nexys2.vhd
new file mode 100644 (file)
index 0000000..d685c47
--- /dev/null
@@ -0,0 +1,86 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library nexys2_lib;
+
+library work;
+
+
+entity test_nexys2 is
+       port (
+               clk_50:   in  std_logic;
+               seg:      out std_logic_vector(6 downto 0);
+               dp:       out std_logic;
+               an:       out std_logic_vector(3 downto 0);
+               sw:       in  std_logic_vector(7 downto 0);
+               btn:      in  std_logic_vector(3 downto 0);
+
+               vgaRed:   out std_logic_vector(3 downto 1);
+               vgaGreen: out std_logic_vector(3 downto 1);
+               vgaBlue:  out std_logic_vector(3 downto 2);
+               Hsync:    out std_logic;
+               Vsync:    out std_logic
+       );
+end test_nexys2;
+
+
+architecture behavioral of test_nexys2 is
+
+       signal clk:        std_logic;
+       signal rst:        std_logic;
+       signal cyc:        std_logic;
+       signal stb:        std_logic;
+       signal we:         std_logic;
+       signal ack:        std_logic;
+       signal adr_master: std_logic_vector(7 downto 0);
+       signal adr_slave:  std_logic_vector(13 downto 0);
+       signal dat_mosi:   std_logic_vector(7 downto 0);
+       signal dat_miso:   std_logic_vector(7 downto 0);
+
+begin
+
+       e_wb: entity nexys2_lib.wishbone_debugger
+               port map (
+                       clk_o  => clk,
+                       rst_o  => rst,
+
+                       cyc_o  => cyc,
+                       stb_o  => stb,
+                       we_o   => we,
+                       ack_i  => ack,
+                       adr_o  => adr_master,
+                       dat_o  => dat_mosi,
+                       dat_i  => dat_miso,
+
+                       clk_50 => clk_50,
+                       seg    => seg,
+                       dp     => dp,
+                       an     => an,
+                       sw     => sw,
+                       btn    => btn
+               );
+
+       adr_slave <= adr_master(7) & "000000" & adr_master(6 downto 0);
+
+       e_dut: entity work.vga_tiler
+               port map (
+                       rst_i    => rst,
+                       clk_i    => clk,
+
+                       cyc_i    => cyc,
+                       stb_i    => stb,
+                       we_i     => we,
+                       ack_o    => ack,
+                       adr_i    => adr_slave,
+                       dat_i    => dat_mosi,
+                       dat_o    => dat_miso,
+
+                       vgaRed   => vgaRed,
+                       vgaGreen => vgaGreen,
+                       vgaBlue  => vgaBlue,
+                       Hsync    => Hsync,
+                       Vsync    => Vsync
+               );
+
+end behavioral;
diff --git a/libraries/vga/vga_counter.vhd b/libraries/vga/vga_counter.vhd
new file mode 100644 (file)
index 0000000..907188e
--- /dev/null
@@ -0,0 +1,184 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity vga_counter is
+       generic (
+               -- Default settings for 640x480, 60Hz with 50MHz clock
+
+               -- Horizontal timings in units of clock cycles
+               -- At 50MHz, 2 clocks per pixel (25MHz pixel clock)
+               H_DISPLAY:     integer := 640*2;
+               H_FRONT_PORCH: integer := 16*2;
+               H_SYNC_WIDTH:  integer := 96*2;
+               H_BACK_PORCH:  integer := 48*2;
+
+               -- Vertical timings in units of lines
+               V_DISPLAY:     integer := 480;
+               V_FRONT_PORCH: integer := 10;
+               V_SYNC_WIDTH:  integer := 2;
+               V_BACK_PORCH:  integer := 29
+       );
+       port (
+               clk_50:  in  std_logic;
+
+               pix_x:   out std_logic_vector(10 downto 0);
+               pix_y:   out std_logic_vector(9 downto 0);
+
+               h_blank: out std_logic;
+               h_sync:  out std_logic;
+
+               v_blank: out std_logic;
+               v_sync:  out std_logic
+       );
+end vga_counter;
+
+
+architecture behavioral of vga_counter is
+
+       constant V_S_START: integer := V_DISPLAY + V_FRONT_PORCH;
+       constant V_S_END:   integer := V_DISPLAY + V_FRONT_PORCH + V_SYNC_WIDTH;
+       constant V_TOTAL:   integer := V_DISPLAY + V_FRONT_PORCH + V_SYNC_WIDTH + V_BACK_PORCH;
+
+       constant H_S_START: integer := H_DISPLAY + H_FRONT_PORCH;
+       constant H_S_END:   integer := H_DISPLAY + H_FRONT_PORCH + H_SYNC_WIDTH;
+       constant H_TOTAL:   integer := H_DISPLAY + H_FRONT_PORCH + H_SYNC_WIDTH + H_BACK_PORCH;
+
+       -- Using what are essentially JK flip flops for blank and sync signals
+       -- allows the signals to be valid immediately after a clock edge rather than
+       -- having to wait for propagation of the count value through comparators
+       -- with long carry chains.
+       -- The monitor probably wouldn't care - video signal timings can be loosey-
+       -- goosey sometimes, but for signalling the rest of the system internally,
+       -- like raster interrupts and such, it's best to keep combo-logic out of the
+       -- output signals when possible.
+
+       -- The pixel clock is 25MHz, so the horizontal count reg has an extra bit
+       -- that should be stripped off when checking the current x position.
+
+       -- There's no reset logic, but if it starts up with the counters out of
+       -- bounds, they'll overflow to zero on a short enough timescale (about one
+       -- frame) that it shouldn't be a problem.
+
+       signal h_count_reg:   unsigned(10 downto 0);
+       signal h_count_end:   std_logic;
+       signal h_blank_reg:   std_logic;
+       signal h_blank_start: std_logic;
+       signal h_blank_end:   std_logic;
+       signal h_sync_reg:    std_logic;
+       signal h_sync_start:  std_logic;
+       signal h_sync_end:    std_logic;
+
+       signal v_count_reg:   unsigned(9 downto 0);
+       signal v_count_en:    std_logic;
+       signal v_count_end:   std_logic;
+       signal v_blank_reg:   std_logic;
+       signal v_blank_start: std_logic;
+       signal v_blank_end:   std_logic;
+       signal v_sync_reg:    std_logic;
+       signal v_sync_start:  std_logic;
+       signal v_sync_end:    std_logic;
+
+begin
+
+       -- Horizontal timing
+
+       process (clk_50, h_count_end, h_count_reg)
+       begin
+               if rising_edge(clk_50) then
+                       if h_count_end = '1' then
+                               h_count_reg <= (others => '0');
+                       else
+                               h_count_reg <= h_count_reg + 1;
+                       end if;
+               end if;
+       end process;
+
+       process (clk_50, h_blank_start, h_blank_end)
+       begin
+               if rising_edge(clk_50) then
+                       if h_blank_start = '1' then
+                               h_blank_reg <= '1';
+                       elsif h_blank_end = '1' then
+                               h_blank_reg <= '0';
+                       end if;
+               end if;
+       end process;
+
+       process (clk_50, h_sync_start, h_sync_end)
+       begin
+               if rising_edge(clk_50) then
+                       if h_sync_start = '1' then
+                               h_sync_reg <= '1';
+                       elsif h_sync_end = '1' then
+                               h_sync_reg <= '0';
+                       end if;
+               end if;
+       end process;
+
+       h_blank_start <= '1' when h_count_reg = H_DISPLAY-1 else '0';
+       h_sync_start  <= '1' when h_count_reg = H_S_START-1 else '0';
+       h_sync_end    <= '1' when h_count_reg = H_S_END-1   else '0';
+       h_blank_end   <= '1' when h_count_reg = H_TOTAL-1   else '0';
+       h_count_end   <= '1' when h_count_reg = H_TOTAL-1   else '0';
+
+
+       -- Advance the line count when the horrizontal sync pulse begins
+       v_count_en <= h_sync_start;
+
+
+       -- Vertical timing
+
+       process (clk_50, v_count_en, v_count_end, v_count_reg)
+       begin
+               if rising_edge(clk_50) and v_count_en = '1' then
+                       if v_count_end = '1' then
+                               v_count_reg <= (others => '0');
+                       else
+                               v_count_reg <= v_count_reg + 1;
+                       end if;
+               end if;
+       end process;
+
+       process (clk_50, v_blank_start, v_blank_end)
+       begin
+               if rising_edge(clk_50) then
+                       if v_blank_start = '1' then
+                               v_blank_reg <= '1';
+                       elsif v_blank_end = '1' then
+                               v_blank_reg <= '0';
+                       end if;
+               end if;
+       end process;
+
+       process (clk_50, v_sync_start, v_sync_end)
+       begin
+               if rising_edge(clk_50) then
+                       if v_sync_start = '1' then
+                               v_sync_reg <= '1';
+                       elsif v_sync_end = '1' then
+                               v_sync_reg <= '0';
+                       end if;
+               end if;
+       end process;
+
+       v_blank_start <= '1' when v_count_reg = V_DISPLAY-1 else '0';
+       v_sync_start  <= '1' when v_count_reg = V_S_START-1 else '0';
+       v_sync_end    <= '1' when v_count_reg = V_S_END-1   else '0';
+       v_blank_end   <= '1' when v_count_reg = V_TOTAL-1   else '0';
+       v_count_end   <= '1' when v_count_reg = V_TOTAL-1   else '0';
+
+
+       -- Output signals
+
+       pix_x   <= std_logic_vector(h_count_reg);
+       pix_y   <= std_logic_vector(v_count_reg);
+
+       h_blank <= h_blank_reg;
+       h_sync  <= h_sync_reg;
+
+       v_blank <= v_blank_reg;
+       v_sync  <= v_sync_reg;
+
+end behavioral;
diff --git a/libraries/vga/vga_tiler.vhd b/libraries/vga/vga_tiler.vhd
new file mode 100644 (file)
index 0000000..ae45160
--- /dev/null
@@ -0,0 +1,530 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+library work;
+
+
+entity vga_tiler is
+       port (
+               -- Wishbone SYSCON
+               rst_i:    in  std_logic;
+               clk_i:    in  std_logic;  -- 50MHz
+
+               -- System access to video ram
+               cyc_i:    in  std_logic;
+               stb_i:    in  std_logic;
+               we_i:     in  std_logic;
+               ack_o:    out std_logic;
+               adr_i:    in  std_logic_vector(13 downto 0);
+               dat_i:    in  std_logic_vector(7 downto 0);
+               dat_o:    out std_logic_vector(7 downto 0);
+
+               -- VGA output
+               vgaRed:   out std_logic_vector(3 downto 1);
+               vgaGreen: out std_logic_vector(3 downto 1);
+               vgaBlue:  out std_logic_vector(3 downto 2);
+               Hsync:    out std_logic;
+               Vsync:    out std_logic
+       );
+end vga_tiler;
+
+
+architecture behavioral of vga_tiler is
+
+       -- System Wishbone interface
+       type wb_state_t is (S_IDLE, S_ACK);
+       signal wb_state_reg:       wb_state_t;
+       signal wb_state_next:      wb_state_t;
+       signal char0_stb_i:        std_logic;
+       signal char1_stb_i:        std_logic;
+       signal char2_stb_i:        std_logic;
+       signal char3_stb_i:        std_logic;
+       signal tile_stb_i:         std_logic;
+       signal char0_dat_o:        std_logic_vector(7 downto 0);
+       signal char1_dat_o:        std_logic_vector(7 downto 0);
+       signal char2_dat_o:        std_logic_vector(7 downto 0);
+       signal char3_dat_o:        std_logic_vector(7 downto 0);
+       signal tile_dat_o:         std_logic_vector(7 downto 0);
+
+       -- BRAM is faster than the external ram, and is dual-ported to prevent bus
+       -- contention with whatever system is attached to the tiler.  BRAM is also
+       -- synchronous, which means it will take multiple clock cycles to process
+       -- each pixel - so a cheeky lil pipeline is needed.  Also, the BRAM cells
+       -- take separate clocks for each interface, which allows the VGA controller
+       -- to be driven at a different clock rate than the rest of the system, which
+       -- allows for new and exciting screen resolutions.
+
+       -- BRAM access is kept in its own separate stages for convenience.  Other
+       -- stages can be further split if higher performance is necessary.  The
+       -- output registers of the BRAM cells are treated as part of their
+       -- respective interstage registers.  Some signals (like all the bits of
+       -- pix_x and pix_y) are passed down the pipe even after they're strictly
+       -- necessary.  This is for convenience if more is added later, and they will
+       -- be trimmed by the specializer during synthesis.
+
+       -- Stage 0: Compute character location within screen buffer
+       signal s0_h_sync:          std_logic;
+       signal s0_v_sync:          std_logic;
+       signal s0_h_blank:         std_logic;
+       signal s0_v_blank:         std_logic;
+       signal s0_pix_x:           std_logic_vector(10 downto 0);
+       signal s0_pix_y:           std_logic_vector(9 downto 0);
+       signal s0_char_row:        unsigned(6 downto 0);
+       signal s0_char_col:        unsigned(6 downto 0);
+       signal s0_char_idx:        unsigned(12 downto 0);
+
+       -- Register 0-1
+       signal s01_h_sync_reg:     std_logic;
+       signal s01_v_sync_reg:     std_logic;
+       signal s01_h_blank_reg:    std_logic;
+       signal s01_v_blank_reg:    std_logic;
+       signal s01_pix_x_reg:      std_logic_vector(10 downto 0);
+       signal s01_pix_y_reg:      std_logic_vector(9 downto 0);
+       signal s01_char_idx_reg:   unsigned(12 downto 0);
+
+       -- Stage 1: Lookup screen character's tile ID within screen buffer
+
+       -- Register 1-2
+       signal s12_h_sync_reg:     std_logic;
+       signal s12_v_sync_reg:     std_logic;
+       signal s12_h_blank_reg:    std_logic;
+       signal s12_v_blank_reg:    std_logic;
+       signal s12_pix_x_reg:      std_logic_vector(10 downto 0);
+       signal s12_pix_y_reg:      std_logic_vector(9 downto 0);
+       signal s12_char_idx_reg:   unsigned(12 downto 0);
+       signal s12_bank0_char_reg: std_logic_vector(7 downto 0);
+       signal s12_bank1_char_reg: std_logic_vector(7 downto 0);
+       signal s12_bank2_char_reg: std_logic_vector(7 downto 0);
+       signal s12_bank3_char_reg: std_logic_vector(7 downto 0);
+
+       -- Stage 2: Select the tile ID from the correct bank and compute tile location within tile ram
+       signal s2_char:            std_logic_vector(7 downto 0);
+       signal s2_tile_idx:        std_logic_vector(10 downto 0);
+
+       -- Register 2-3
+       signal s23_h_sync_reg:     std_logic;
+       signal s23_v_sync_reg:     std_logic;
+       signal s23_h_blank_reg:    std_logic;
+       signal s23_v_blank_reg:    std_logic;
+       signal s23_pix_x_reg:      std_logic_vector(10 downto 0);
+       signal s23_pix_y_reg:      std_logic_vector(9 downto 0);
+       signal s23_tile_idx_reg:   std_logic_vector(10 downto 0);
+
+       -- Stage 3: Lookup tile slice within tile ram
+
+       -- Register 3-4
+       signal s34_h_sync_reg:     std_logic;
+       signal s34_v_sync_reg:     std_logic;
+       signal s34_h_blank_reg:    std_logic;
+       signal s34_v_blank_reg:    std_logic;
+       signal s34_pix_x_reg:      std_logic_vector(10 downto 0);
+       signal s34_pix_y_reg:      std_logic_vector(9 downto 0);
+       signal s34_tile_line_reg:  std_logic_vector(7 downto 0);
+
+       -- Stage 4: Select bit within tile slice an map to color
+       signal s4_bit:             std_logic;
+       signal s4_visible:         std_logic;
+       signal s4_vga_red:         std_logic_vector(3 downto 1);
+       signal s4_vga_green:       std_logic_vector(3 downto 1);
+       signal s4_vga_blue:        std_logic_vector(3 downto 2);
+
+       -- Register 4-output
+       signal s4o_h_sync_reg:     std_logic;
+       signal s4o_v_sync_reg:     std_logic;
+       signal s4o_vga_red_reg:    std_logic_vector(3 downto 1);
+       signal s4o_vga_green_reg:  std_logic_vector(3 downto 1);
+       signal s4o_vga_blue_reg:   std_logic_vector(3 downto 2);
+
+begin
+
+       ----------------------------------------------------------------------------
+       -- System Wishbone interface
+       -- FIXME: this may do double reads/writes - not a problem for now though
+
+
+       process (rst_i, clk_i, wb_state_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               wb_state_reg <= S_IDLE;
+                       else
+                               wb_state_reg <= wb_state_next;
+                       end if;
+               end if;
+       end process;
+
+       process (wb_state_reg, stb_i)
+       begin
+               wb_state_next <= wb_state_reg;
+               ack_o         <= '0';
+
+               case wb_state_reg is
+                       when S_IDLE =>
+                               if stb_i = '1' then
+                                       wb_state_next <= S_ACK;
+                               end if;
+
+                       when S_ACK =>
+                               ack_o         <= '1';
+                               wb_state_next <= S_IDLE;
+
+                       when others =>
+                               wb_state_next <= S_IDLE;
+               end case;
+       end process;
+
+       with adr_i(13 downto 11) select dat_o <=
+               char0_dat_o     when "000",
+               char1_dat_o     when "001",
+               char2_dat_o     when "010",
+               char3_dat_o     when "011",
+               tile_dat_o      when "100",
+               (others => '1') when others;
+
+       char0_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "000" else '0';
+       char1_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "001" else '0';
+       char2_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "010" else '0';
+       char3_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "011" else '0';
+       tile_stb_i  <= stb_i and cyc_i when adr_i(13 downto 11) = "100" else '0';
+
+
+       ----------------------------------------------------------------------------
+       -- (Stage -1: VGA pixel counter and signal generation)
+
+       e_vga_counter: entity work.vga_counter
+               port map (
+                       clk_50  => clk_i,
+
+                       pix_x   => s0_pix_x,
+                       pix_y   => s0_pix_y,
+
+                       h_blank => s0_h_blank,
+                       v_blank => s0_v_blank,
+
+                       h_sync  => s0_h_sync,
+                       v_sync  => s0_v_sync
+               );
+
+
+       ----------------------------------------------------------------------------
+       -- Stage 0: Compute character location within screen buffer
+
+       -- Divide pixel positions by 8 to get row and column
+       s0_char_col <= unsigned(s0_pix_x(10 downto 4));
+       s0_char_row <= unsigned(s0_pix_y(9 downto 3));
+
+       -- Index into screen character buffer is row * 80 + col
+       -- Shouldn't carry for pix_x and pix_y within visible screen range, 13 bits is enough
+       -- Used for reads only, so nondestructive if out of visible screen range
+       s0_char_idx <= ((("00" & s0_char_row) + (s0_char_row & "00")) & "0000") + s0_char_col;
+
+
+       -- Register 0-1
+       process (clk_i, s0_h_sync, s0_v_sync, s0_h_blank, s0_v_blank, s0_pix_x, s0_pix_y, s0_char_idx)
+       begin
+               if rising_edge(clk_i) then
+                       s01_h_sync_reg   <= s0_h_sync;
+                       s01_v_sync_reg   <= s0_v_sync;
+                       s01_h_blank_reg  <= s0_h_blank;
+                       s01_v_blank_reg  <= s0_v_blank;
+                       s01_pix_x_reg    <= s0_pix_x;
+                       s01_pix_y_reg    <= s0_pix_y;
+                       s01_char_idx_reg <= s0_char_idx;
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Stage 1: Look up screen character's tile ID within screen buffer
+
+
+       e_char_bank_0: ramb16_s9_s9
+               generic map (
+                       INIT_00 => x"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100",
+                       INIT_01 => x"3f3e3d3c3b3a393837363534333231302f2e2d2c2b2a29282726252423222120",
+                       INIT_02 => x"5f5e5d5c5b5a595857565554535251504f4e4d4c4b4a49484746454443424140",
+                       INIT_03 => x"7f7e7d7c7b7a797877767574737271706f6e6d6c6b6a69686766656463626160",
+                       INIT_04 => x"9f9e9d9c9b9a999897969594939291908f8e8d8c8b8a89888786858483828180",
+                       INIT_05 => x"bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a0",
+                       INIT_06 => x"dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0",
+                       INIT_07 => x"fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0"
+               )
+               port map (
+                       -- Port A, tiler access
+                       ssra  => rst_i,
+                       clka  => clk_i,
+                       ena   => '1',
+                       wea   => '0',
+                       addra => std_logic_vector(s01_char_idx_reg(10 downto 0)),
+
+                       doa   => s12_bank0_char_reg,
+                       dopa  => open,
+                       dia   => (others => '0'),
+                       dipa  => "0",
+
+                       -- Port B, system access
+                       ssrb  => rst_i,
+                       clkb  => clk_i,
+                       enb   => char0_stb_i,
+                       web   => we_i,
+                       addrb => adr_i(10 downto 0),
+
+                       dob   => char0_dat_o,
+                       dopb  => open,
+                       dib   => dat_i,
+                       dipb  => "0"
+               );
+
+       e_char_bank_1: ramb16_s9_s9
+               port map (
+                       -- Port A, tiler access
+                       ssra  => rst_i,
+                       clka  => clk_i,
+                       ena   => '1',
+                       wea   => '0',
+                       addra => std_logic_vector(s01_char_idx_reg(10 downto 0)),
+
+                       doa   => s12_bank1_char_reg,
+                       dopa  => open,
+                       dia   => (others => '0'),
+                       dipa  => "0",
+
+                       -- Port B, system access
+                       ssrb  => rst_i,
+                       clkb  => clk_i,
+                       enb   => char1_stb_i,
+                       web   => we_i,
+                       addrb => adr_i(10 downto 0),
+
+                       dob   => char1_dat_o,
+                       dopb  => open,
+                       dib   => dat_i,
+                       dipb  => "0"
+               );
+
+       e_char_bank_2: ramb16_s9_s9
+               port map (
+                       -- Port A, tiler access
+                       ssra  => rst_i,
+                       clka  => clk_i,
+                       ena   => '1',
+                       wea   => '0',
+                       addra => std_logic_vector(s01_char_idx_reg(10 downto 0)),
+
+                       doa   => s12_bank2_char_reg,
+                       dopa  => open,
+                       dia   => (others => '0'),
+                       dipa  => "0",
+
+                       -- Port B, system access
+                       ssrb  => rst_i,
+                       clkb  => clk_i,
+                       enb   => char2_stb_i,
+                       web   => we_i,
+                       addrb => adr_i(10 downto 0),
+
+                       dob   => char2_dat_o,
+                       dopb  => open,
+                       dib   => dat_i,
+                       dipb  => "0"
+               );
+
+       e_char_bank_3: ramb16_s9_s9
+               port map (
+                       -- Port A, tiler access
+                       ssra  => rst_i,
+                       clka  => clk_i,
+                       ena   => '1',
+                       wea   => '0',
+                       addra => std_logic_vector(s01_char_idx_reg(10 downto 0)),
+
+                       doa   => s12_bank3_char_reg,
+                       dopa  => open,
+                       dia   => (others => '0'),
+                       dipa  => "0",
+
+                       -- Port B, system access
+                       ssrb  => rst_i,
+                       clkb  => clk_i,
+                       enb   => char3_stb_i,
+                       web   => we_i,
+                       addrb => adr_i(10 downto 0),
+
+                       dob   => char3_dat_o,
+                       dopb  => open,
+                       dib   => dat_i,
+                       dipb  => "0"
+               );
+
+
+       -- Register 1-2
+       process (clk_i, s01_h_sync_reg, s01_v_sync_reg, s01_h_blank_reg, s01_v_blank_reg, s01_pix_x_reg, s01_pix_y_reg, s01_char_idx_reg)
+       begin
+               if rising_edge(clk_i) then
+                       s12_h_sync_reg   <= s01_h_sync_reg;
+                       s12_v_sync_reg   <= s01_v_sync_reg;
+                       s12_h_blank_reg  <= s01_h_blank_reg;
+                       s12_v_blank_reg  <= s01_v_blank_reg;
+                       s12_pix_x_reg    <= s01_pix_x_reg;
+                       s12_pix_y_reg    <= s01_pix_y_reg;
+                       s12_char_idx_reg <= s01_char_idx_reg;
+                       -- s12_bank0_char_reg
+                       -- s12_bank1_char_reg
+                       -- s12_bank2_char_reg
+                       -- s12_bank3_char_reg
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Stage 2: Select the tile ID from the correct bank and compute tile location within tile ram
+
+
+       with s12_char_idx_reg(12 downto 11) select s2_char <=
+               s12_bank0_char_reg when "00",
+               s12_bank1_char_reg when "01",
+               s12_bank2_char_reg when "10",
+               s12_bank3_char_reg when "11",
+               (others => 'X')    when others;
+
+       s2_tile_idx <= s2_char & s12_pix_y_reg(2 downto 0);
+
+
+       -- Register 2-3
+       process (clk_i, s12_h_sync_reg, s12_v_sync_reg, s12_h_blank_reg, s12_v_blank_reg, s12_pix_x_reg, s12_pix_y_reg, s2_tile_idx)
+       begin
+               if rising_edge(clk_i) then
+                       s23_h_sync_reg   <= s12_h_sync_reg;
+                       s23_v_sync_reg   <= s12_v_sync_reg;
+                       s23_h_blank_reg  <= s12_h_blank_reg;
+                       s23_v_blank_reg  <= s12_v_blank_reg;
+                       s23_pix_x_reg    <= s12_pix_x_reg;
+                       s23_pix_y_reg    <= s12_pix_y_reg;
+                       s23_tile_idx_reg <= s2_tile_idx;
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Stage 3: Lookup tile slice within tile ram
+
+
+       e_tile_ram: ramb16_s9_s9
+               generic map (
+                       INIT_08 => x"006666ff66ff6666000000000066666600180000181818180000000000000000",
+                       INIT_09 => x"0000000000180c06003f6667383c663c00466630180c666200187c063c603e18",
+                       INIT_0A => x"000018187e1818000000663cff3c66000030180c0c0c1830000c18303030180c",
+                       INIT_0B => x"006030180c0603000018180000000000000000007e0000003018180000000000",
+                       INIT_0C => x"003c66061c06663c007e60300c06663c007e181818381818003c6666766e663c",
+                       INIT_0D => x"00181818180c667e003c66667c60663c003c6606067c607e0006067f661e0e06",
+                       INIT_0E => x"30181800001800000000180000180000003c66063e66663c003c66663c66663c",
+                       INIT_0F => x"001800180c06663c0070180c060c18700000007e007e0000000e18306030180e",
+                       INIT_10 => x"003c66606060663c007c66667c66667c006666667e663c18003c62606e6e663c",
+                       INIT_11 => x"003c66666e60663c006060607860607e007e60607860607e00786c6666666c78",
+                       INIT_12 => x"00666c7870786c6600386c0c0c0c0c1e003c18181818183c006666667e666666",
+                       INIT_13 => x"003c66666666663c0066666e7e7e7666006363636b7f7763007e606060606060",
+                       INIT_14 => x"003c66063c60663c00666c787c66667c000e3c666666663c006060607c66667c",
+                       INIT_15 => x"0063777f6b63636300183c6666666666003c666666666666001818181818187e",
+                       INIT_16 => x"003c30303030303c007e6030180c067e001818183c6666660066663c183c6666",
+                       INIT_17 => x"00000000000000000000000000000000003c0c0c0c0c0c3c0000000000000000",
+                       INIT_18 => x"003c6060603c0000007c66667c606000003e663e063c00000000000000000000",
+                       INIT_19 => x"7c063e66663e0000001818183e180e00003c607e663c0000003e66663e060600",
+                       INIT_1A => x"00666c786c6060003c06060606000600003c181838001800006666667c606000",
+                       INIT_1B => x"003c6666663c000000666666667c000000636b7f7f660000003c181818183800",
+                       INIT_1C => x"007c063c603e000000606060667c000006063e66663e000060607c66667c0000",
+                       INIT_1D => x"00363e7f6b63000000183c6666660000003e666666660000000e1818187e1800",
+                       INIT_1E => x"0000000000000000007e30180c7e0000780c3e666666000000663c183c660000"
+               )
+               port map (
+                       -- Port A, tiler access
+                       ssra  => rst_i,
+                       clka  => clk_i,
+                       ena   => '1',
+                       wea   => '0',
+                       addra => s23_tile_idx_reg,
+
+                       doa   => s34_tile_line_reg,
+                       dopa  => open,
+                       dia   => (others => '0'),
+                       dipa  => "0",
+
+                       -- Port B, system access
+                       ssrb  => rst_i,
+                       clkb  => clk_i,
+                       enb   => tile_stb_i,
+                       web   => we_i,
+                       addrb => adr_i(10 downto 0),
+
+                       dob   => tile_dat_o,
+                       dopb  => open,
+                       dib   => dat_i,
+                       dipb  => "0"
+               );
+
+
+       -- Register 3-4
+       process (clk_i, s23_h_sync_reg, s23_v_sync_reg, s23_h_blank_reg, s23_v_blank_reg, s23_pix_x_reg, s23_pix_y_reg)
+       begin
+               if rising_edge(clk_i) then
+                       s34_h_sync_reg  <= s23_h_sync_reg;
+                       s34_v_sync_reg  <= s23_v_sync_reg;
+                       s34_h_blank_reg <= s23_h_blank_reg;
+                       s34_v_blank_reg <= s23_v_blank_reg;
+                       s34_pix_x_reg   <= s23_pix_x_reg;
+                       s34_pix_y_reg   <= s23_pix_y_reg;
+                       -- s34_tile_line_reg
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Stage 4: Select bit within tile slice an map to color
+
+
+       --with s34_pix_x_reg(3 downto 1) select s4_bit <=
+       --      s34_tile_line_reg(7) when "000",
+       --      s34_tile_line_reg(6) when "001",
+       --      s34_tile_line_reg(5) when "010",
+       --      s34_tile_line_reg(4) when "011",
+       --      s34_tile_line_reg(3) when "100",
+       --      s34_tile_line_reg(2) when "101",
+       --      s34_tile_line_reg(1) when "110",
+       --      s34_tile_line_reg(0) when others;
+
+       s4_bit       <= s34_tile_line_reg(to_integer(unsigned(not s34_pix_x_reg(3 downto 1))));
+       s4_visible   <= not (s34_h_blank_reg or s34_v_blank_reg);
+
+       s4_vga_red   <= "111" when (s4_visible and s4_bit) = '1' else "000";
+       s4_vga_green <= "111" when (s4_visible and s4_bit) = '1' else "000";
+       s4_vga_blue  <= "11"  when (s4_visible and s4_bit) = '1' else "00";
+
+
+       -- Register 4-output
+       process (clk_i, s34_h_sync_reg, s34_v_sync_reg, s4_vga_red, s4_vga_green, s4_vga_blue)
+       begin
+               if rising_edge(clk_i) then
+                       s4o_h_sync_reg    <= s34_h_sync_reg;
+                       s4o_v_sync_reg    <= s34_v_sync_reg;
+                       s4o_vga_red_reg   <= s4_vga_red;
+                       s4o_vga_green_reg <= s4_vga_green;
+                       s4o_vga_blue_reg  <= s4_vga_blue;
+               end if;
+       end process;
+
+
+       ----------------------------------------------------------------------------
+       -- Output
+
+
+       Hsync    <= s4o_h_sync_reg;
+       Vsync    <= s4o_v_sync_reg;
+       vgaRed   <= s4o_vga_red_reg;
+       vgaGreen <= s4o_vga_green_reg;
+       vgaBlue  <= s4o_vga_blue_reg;
+
+end behavioral;
diff --git a/projects/cpu_0/asm/as.py b/projects/cpu_0/asm/as.py
new file mode 100755 (executable)
index 0000000..8db13b8
--- /dev/null
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+
+import argparse
+import struct
+
+
+# ------------------------------------------------------------------------------
+
+
+def assemble(source: str) -> bytearray:
+    b = bytearray()
+    syms = dict()
+    forwards = list()
+    tokens = source.split()
+    instrs = {
+        'nop':  b'\x00',
+        '#8':   b'\x01', '#32':  b'\x02',
+        '@8':   b'\x03', '@32':  b'\x04',
+        '!8':   b'\x05', '!32':  b'\x06',
+        'r@':   b'\x07', '>r':   b'\x08', 'r>':   b'\x09',
+        'call': b'\x0a', ';':    b'\x0b',
+        'jmp':  b'\x0c', 'jz':   b'\x0d', 'jn':   b'\x0e', 'jp':   b'\x0f',
+        '+':    b'\x10', '-':    b'\x11',
+        '&':    b'\x12', '|':    b'\x13', '^':    b'\x14', '~':    b'\x15',
+        'dup':  b'\x16', 'drop': b'\x17', 'swap': b'\x18',
+        'ien':  b'\x19', 'idis': b'\x1a',
+    }
+
+    comment = False
+    for token in tokens:
+        token = token.lower()
+
+        if comment:
+            if token == ')':
+                comment = False
+            continue
+
+        if token == '(':
+            comment = True
+        elif token in instrs:
+            b += instrs[token]
+        elif '=' in token:
+            sym, value = token.split('=', maxsplit=1)
+            value = int(value, 0)
+            syms[sym] = value
+        elif token.endswith(':'):
+            assert token[0] in '_abcdefghijklmnopqrstuvwxyz'
+            syms[token[:-1]] = len(b)
+        elif token[0] in '-0123456789':
+            if token.endswith('i8'):
+                val = int(token[:-2], 0)
+                assert 0 <= val <= 255
+                b += bytes([val])
+            elif token.endswith('i32'):
+                val = int(token[:-3], 0)
+                assert -0x80000000 <= val <= 0xffffffff
+                if val < 0:
+                    b += struct.pack('<i', val)
+                else:
+                    b += struct.pack('<I', val)
+            else:
+                print('Number must end with "i8" or "i32"')
+                assert False
+        elif token[0] in '_abcdefghijklmnopqrstuvwxyz':
+            if token in syms:
+                b += struct.pack('<I', syms[token])
+            else:
+                forwards.append((len(b), token))
+                b += b'\x00\x00\x00\x00'
+        elif token.startswith('"') and token.endswith('"'):
+            b += bytes(ord(c) for c in token[1:-1])
+
+    # Align to two-byte words
+    if len(b) % 2 == 1:
+        b += b'\xff'
+
+    # Resolve forward references
+    for offset, symbol in forwards:
+        assert symbol in syms
+        b[offset:offset+4] = struct.pack('<I', syms[symbol])
+    return b
+
+
+# ------------------------------------------------------------------------------
+
+
+def main() -> int:
+    parser = argparse.ArgumentParser('AS - CPU0 Assembler')
+    parser.add_argument('--outfile', '-o', help='Output filename')
+    parser.add_argument('filename', help='Source filename')
+    args = parser.parse_args()
+
+    with open(args.filename, 'r') as f:
+        with open(args.outfile, 'wb') as g:
+            s = f.read()
+            b = assemble(s)
+            g.write(b)
+
+    return 0
+
+
+if __name__ == '__main__':
+    exit(main())
diff --git a/projects/cpu_0/asm/hellorld.asm b/projects/cpu_0/asm/hellorld.asm
new file mode 100644 (file)
index 0000000..70eba67
--- /dev/null
@@ -0,0 +1,31 @@
+( host registers )
+host_ctrl=0x02004000
+host_flags=0x02004001
+host_mbox=0x02004002
+host_swled=0x02004003
+host_sseg0=0x02004004
+host_sseg1=0x02004005
+host_sseg2=0x02004006
+host_sseg3=0x02004007
+
+( ps2 registers )
+ps2_ctrl=0x02004008
+ps2_imask=0x02004009
+ps2_iflag=0x0200400a
+ps2_error=0x0200400b
+ps2_data=0x0200400c
+
+( rs232 registers )
+rs232_ctrl=0x02004010
+rs232_baudl=0x02004011
+rs232_baudh=0x02004012
+rs232_imask=0x02004013
+rs232_iflag=0x02004014
+rs232_data=0x02004015
+
+( ivec -- )
+isr:
+
+halt:
+       #8 0xa5i8 drop
+       jmp halt
diff --git a/projects/cpu_0/asm/test.asm b/projects/cpu_0/asm/test.asm
new file mode 100644 (file)
index 0000000..7470ba5
--- /dev/null
@@ -0,0 +1,121 @@
+( host registers )
+host_ctrl=0x02004000
+host_flags=0x02004001
+host_mbox=0x02004002
+host_swled=0x02004003
+host_sseg0=0x02004004
+host_sseg1=0x02004005
+host_sseg2=0x02004006
+host_sseg3=0x02004007
+
+( ps2 registers )
+ps2_ctrl=0x02004008
+ps2_imask=0x02004009
+ps2_iflag=0x0200400a
+ps2_error=0x0200400b
+ps2_data=0x0200400c
+
+( rs232 registers )
+rs232_ctrl=0x02004010
+rs232_baudl=0x02004011
+rs232_baudh=0x02004012
+rs232_imask=0x02004013
+rs232_iflag=0x02004014
+rs232_data=0x02004015
+
+( ivec -- )
+isr:
+    ( if ivec > 15 goto isr_unknown )
+       dup #8 15i8 - jn isr_unknown
+
+    ( jmp [isr_jumptable + ivec*4] )
+    dup + dup + #32 isr_jmptable + @32 >r ;
+
+isr_jmptable:
+    isr_0 isr_1 isr_2 isr_3
+    isr_4 isr_5 isr_6 isr_7
+    isr_8 isr_9 isr_a isr_b
+    isr_c isr_d isr_e isr_f
+
+isr_unknown:
+    drop ien ;
+
+( reset )
+isr_0:
+    ( write something to seven seg display )
+    #8 0x3ci8 #32 host_sseg0 !8
+    #8 0xa5i8 #32 host_sseg1 !8
+    #8 0x5ai8 #32 host_sseg2 !8
+    #8 0xc3i8 #32 host_sseg3 !8
+
+    ( write 0x1458 to baud register - 9600 baud )
+    #8 0x14i8 #32 rs232_baudl !8
+    #8 0x58i8 #32 rs232_baudh !8
+
+    ( enable rx ready interrupt )
+    #8 0x01i8 #32 rs232_imask !8
+
+    ( enable transmitter and receiver )
+    #8 0x03i8 #32 rs232_ctrl !8
+
+    ( Say hi )
+    #8 0x41i8 #32 rs232_data !8
+
+    jmp halt
+
+( host mailbox flag interrupt )
+isr_1:
+    ien ;
+
+( ps2 interrupt )
+isr_2:
+    ien ;
+
+( uart interrupt )
+isr_3:
+    ( if [rs232_iflag] & 0x01 == 0x00 then return )
+    #32 rs232_iflag @8 #8 0x01i8 & jz _isr_3_ret
+
+    ( [rs232_data] = [rs232_data] )
+    #32 rs232_data dup @8 !8
+_isr_3_ret:
+    ien ;
+
+isr_4:
+    ien ;
+
+isr_5:
+    ien ;
+
+isr_6:
+    ien ;
+
+isr_7:
+    ien ;
+
+isr_8:
+    ien ;
+
+isr_9:
+    ien ;
+
+isr_a:
+    ien ;
+
+isr_b:
+    ien ;
+
+isr_c:
+    ien ;
+
+isr_d:
+    ien ;
+
+isr_e:
+    ien ;
+
+isr_f:
+    ien ;
+
+halt:
+       jmp halt
diff --git a/projects/cpu_0/cpu.vhd b/projects/cpu_0/cpu.vhd
new file mode 100644 (file)
index 0000000..e283b24
--- /dev/null
@@ -0,0 +1,943 @@
+--------------------------------------------------------------------------------
+--
+--    +---+   +---+   +---+   +---+   +---+
+--    | T |   | R |   |MDR|   | I |   |PC |
+--    +---+   +---+   +---+   +---+   +---+
+--    | N |   :   :
+--    +---+
+--    :   :
+--
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+
+entity cpu is
+       port (
+               rst_i: in  std_logic;
+               clk_i: in  std_logic;
+
+               cyc_o: out std_logic;
+               stb_o: out std_logic;
+               we_o:  out std_logic;
+               ack_i: in  std_logic;
+               adr_o: out std_logic_vector(31 downto 0);
+               dat_o: out std_logic_vector(7 downto 0);
+               dat_i: in  std_logic_vector(7 downto 0);
+
+               int_i: in  std_logic;
+               vec_i: in  std_logic_vector(7 downto 0);
+
+               debug_i: in  std_logic_vector(63 downto 0);
+               debug_o: out std_logic_vector(63 downto 0)
+       );
+end cpu;
+
+
+architecture behavioral of cpu is
+
+       type state_t is (
+               S_WAIT,  S_FETCH_2,
+               S_RESET, S_FETCH, S_MEMORY,
+               S_IMM8,  S_IMM32_0, S_IMM32_1, S_IMM32_2, S_IMM32_3,
+               S_LD8,   S_LD32_0,  S_LD32_1,  S_LD32_2,  S_LD32_3,
+               S_ST8,   S_ST32_0,  S_ST32_1,  S_ST32_2,  S_ST32_3,
+               S_EXEC,  S_INT
+       );
+
+       type mem_op_t  is (MEM_IMM8, MEM_IMM32, MEM_LD8, MEM_LD32, MEM_ST8, MEM_ST32, MEM_NONE);
+       type adr_sel_t is (ADR_PC, ADR_T, ADR_T0, ADR_T1, ADR_T2, ADR_T3);
+       type dat_sel_t is (DAT_N0, DAT_N1, DAT_N2, DAT_N3);
+       type alu_op_t  is (ALU_T, ALU_N, ALU_ADD, ALU_SUB, ALU_AND, ALU_OR, ALU_XOR, ALU_NOT, ALU_R, ALU_MDR, ALU_INTVEC);
+       type r_sel_t   is (R_T, R_PC, R_NC);
+       type pc_sel_t  is (PC_MDR, PC_R, PC_INC, PC_NC, PC_INTVEC);
+       type stackop_t is (ST_INC, ST_DEC, ST_NC);
+       type cond_t    is (C_NEG, C_ZERO, C_POS, C_NONE);
+
+       type instr_decode_t is record
+               -- Memory operation
+               mem_op:    mem_op_t;
+
+               -- Stack updates
+               s_op:      stackop_t;
+               r_op:      stackop_t;
+
+               -- Datapath multiplexion
+               alu_op:    alu_op_t;   -- selects new T
+               t_to_n:    std_logic;  -- T->N (push)
+               r_sel:     r_sel_t;    -- selects new R
+               pc_sel:    pc_sel_t;   -- selects new PC
+
+               imask_set: std_logic;
+               imask_clr: std_logic;
+               cond:      cond_t;
+       end record;
+
+       -- Control FSM
+       signal state_reg:  state_t;
+       signal state_next: state_t;
+       signal adr_sel:    adr_sel_t;
+
+       -- Instruction register
+       signal ins_reg:    std_logic_vector(7 downto 0);
+       signal ins_ld:     std_logic;
+       signal ins_decode: instr_decode_t;
+       signal dat_sel:    dat_sel_t;
+
+       -- Program counter
+       signal pc_reg:     std_logic_vector(31 downto 0);
+       signal pc_next:    std_logic_vector(31 downto 0);
+       signal pc_sel:     pc_sel_t;
+
+       -- Memory data register
+       signal mdr_reg:    std_logic_vector(31 downto 0);
+       signal mdr_ld_0:   std_logic;
+       signal mdr_ld_1:   std_logic;
+       signal mdr_ld_2:   std_logic;
+       signal mdr_ld_3:   std_logic;
+
+       -- Parameter stack
+       signal s_ptr_reg:  unsigned(7 downto 0);
+       signal s_ptr_next: unsigned(7 downto 0);
+       signal s_ptr_op:   stackop_t;
+       signal s_ptr_ld:   std_logic;
+       signal s_write:    std_logic;
+       signal s_n_reg:    std_logic_vector(31 downto 0);
+       signal s_t_reg:    std_logic_vector(31 downto 0);
+       signal s_t_new:    std_logic_vector(31 downto 0);
+       signal s_t_ld:     std_logic;
+
+       -- Return stack
+       signal r_ptr_reg:  unsigned(7 downto 0);
+       signal r_ptr_next: unsigned(7 downto 0);
+       signal r_ptr_op:   stackop_t;
+       signal r_ptr_ld:   std_logic;
+       signal r_write:    std_logic;
+       signal r_tos_reg:  std_logic_vector(31 downto 0);
+       signal r_tos_new:  std_logic_vector(31 downto 0);
+
+       signal addra:      std_logic_vector(8 downto 0);
+       signal addrb:      std_logic_vector(8 downto 0);
+
+       -- Kludged-in to allow S_INT logic to override the values from ins_decode
+       signal r_sel:      r_sel_t;
+       signal alu_op:     alu_op_t;
+       signal imask_reg:  std_logic;
+       signal imask_set:  std_logic;
+       signal imask_clr:  std_logic;
+
+       signal n:          std_logic;
+       signal z:          std_logic;
+       signal p:          std_logic;
+       signal ins_pc_sel: pc_sel_t;
+
+       signal debug_reg:  std_logic_vector(15 downto 0);
+
+begin
+
+       -- Control state machine
+       process (rst_i, clk_i, state_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               state_reg <= S_RESET;
+                       else
+                               state_reg <= state_next;
+                       end if;
+               end if;
+       end process;
+
+       debug_o(63 downto 32) <= pc_reg;
+       debug_o(15 downto  8) <= dat_i;
+       debug_o(23 downto 16) <= mdr_reg(7 downto 0);
+       debug_o( 7 downto  0) <= ins_reg;
+
+       process (state_reg, ack_i, ins_decode, int_i, ins_pc_sel, imask_reg, debug_i)
+       begin
+               state_next <= state_reg;
+               cyc_o      <= '0';
+               stb_o      <= '0';
+               we_o       <= '0';
+               adr_sel    <= ADR_PC;
+               ins_ld     <= '0';
+               pc_sel     <= PC_NC;
+               mdr_ld_0   <= '0';
+               mdr_ld_1   <= '0';
+               mdr_ld_2   <= '0';
+               mdr_ld_3   <= '0';
+               dat_sel    <= DAT_N0;
+               s_t_ld     <= '0';
+               s_write    <= '0';
+               r_write    <= '0';
+               s_ptr_op   <= ST_NC;
+               r_ptr_op   <= ST_NC;
+               s_ptr_ld   <= '0';
+               r_ptr_ld   <= '0';
+               r_sel      <= ins_decode.r_sel;
+               alu_op     <= ins_decode.alu_op;
+               imask_set  <= '0';
+               imask_clr  <= '0';
+
+               case state_reg is
+                       when S_FETCH =>
+                               if debug_i(0) = '1' then
+                                       state_next <= S_WAIT;
+                               end if;
+
+                       when S_WAIT =>
+                               if debug_i(0) = '0' then
+                                       state_next <= S_FETCH_2;
+                               end if;
+
+                       when S_FETCH_2 =>
+                               adr_sel <= ADR_PC;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               ins_ld  <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_MEMORY;
+                               end if;
+
+                       when S_MEMORY =>
+                               -- Mask interrupts now if the instruction requests it
+                               -- The mask flag register will be set in the following clock cycle,
+                               -- so this prevents us from having to check ins_decode.imask_set
+                               -- in the S_EXEC state, which would otherwise be necessary to make
+                               -- sure we don't enter an interrupt after a "mask-int" instruction
+                               -- and then return with interrupts unmasked into a critical section
+                               imask_set <= ins_decode.imask_set;
+
+                               case ins_decode.mem_op is
+                                       when MEM_IMM8  => state_next <= S_IMM8;
+                                       when MEM_IMM32 => state_next <= S_IMM32_0;
+                                       when MEM_LD8   => state_next <= S_LD8;
+                                       when MEM_LD32  => state_next <= S_LD32_0;
+                                       when MEM_ST8   => state_next <= S_ST8;
+                                       when MEM_ST32  => state_next <= S_ST32_0;
+                                       when others    => state_next <= S_EXEC;
+                               end case;
+
+                       when S_IMM8 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_0 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_IMM32_0 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_0 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_IMM32_1;
+                               end if;
+
+                       when S_IMM32_1 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_1 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_IMM32_2;
+                               end if;
+
+                       when S_IMM32_2 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_2 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_IMM32_3;
+                               end if;
+
+                       when S_IMM32_3 =>
+                               adr_sel  <= ADR_PC;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_3 <= '1';
+
+                               if ack_i = '1' then
+                                       pc_sel     <= PC_INC;
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_LD8 =>
+                               adr_sel  <= ADR_T;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_0 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_LD32_0 =>
+                               adr_sel  <= ADR_T0;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_0 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_LD32_1;
+                               end if;
+
+                       when S_LD32_1 =>
+                               adr_sel  <= ADR_T1;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_1 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_LD32_2;
+                               end if;
+
+                       when S_LD32_2 =>
+                               adr_sel  <= ADR_T2;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_2 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_LD32_3;
+                               end if;
+
+                       when S_LD32_3 =>
+                               adr_sel  <= ADR_T3;
+                               cyc_o    <= '1';
+                               stb_o    <= '1';
+                               mdr_ld_3 <= '1';
+
+                               if ack_i = '1' then
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_ST8 =>
+                               adr_sel <= ADR_T;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N0;
+
+                               if ack_i = '1' then
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_ST32_0 =>
+                               adr_sel <= ADR_T0;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N0;
+
+                               if ack_i = '1' then
+                                       state_next <= S_ST32_1;
+                               end if;
+
+                       when S_ST32_1 =>
+                               adr_sel <= ADR_T1;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N1;
+
+                               if ack_i = '1' then
+                                       state_next <= S_ST32_2;
+                               end if;
+
+                       when S_ST32_2 =>
+                               adr_sel <= ADR_T2;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N2;
+
+                               if ack_i = '1' then
+                                       state_next <= S_ST32_3;
+                               end if;
+
+                       when S_ST32_3 =>
+                               adr_sel <= ADR_T3;
+                               cyc_o   <= '1';
+                               stb_o   <= '1';
+                               we_o    <= '1';
+                               dat_sel <= DAT_N3;
+
+                               if ack_i = '1' then
+                                       state_next <= S_EXEC;
+                               end if;
+
+                       when S_EXEC =>
+                               -- At this point, MDR will contain any immediate or loaded value and any stores have been performed
+
+                               -- Load T with new value
+                               s_t_ld <= '1';
+
+                               -- Load N with new value
+                               if ins_decode.t_to_n = '1' then
+                                       s_write <= '1';
+                               end if;
+
+                               -- Load R with new value
+                               if ins_decode.r_sel /= R_NC then
+                                       r_write <= '1';
+                               end if;
+
+                               -- Load PC with new value
+                               -- ins_pc_sel includes conditions
+                               pc_sel <= ins_pc_sel;
+
+                               -- Use updated stack pointers
+                               s_ptr_op <= ins_decode.s_op;
+                               r_ptr_op <= ins_decode.r_op;
+                               s_ptr_ld <= '1';
+                               r_ptr_ld <= '1';
+
+                               -- Clear the interrupt mask flag if necessary
+                               -- The flag will be cleared in the next clock cycle, so the next
+                               -- instruction is guaranteed to be executed if an interrupt is
+                               -- pending, useful for clearing the interrupt flag and then returning
+                               imask_clr <= ins_decode.imask_clr;
+
+                               -- Check for interrupts during the execute step so we don't have any in-progress memory operations to cancel
+                               if int_i = '1' and imask_reg = '0' then
+                                       state_next <= S_INT;
+                               else
+                                       state_next <= S_FETCH;
+                               end if;
+
+                       when S_INT =>
+                               -- Load T with the vector value, load N with old T, push down the stack
+                               alu_op     <= ALU_INTVEC;
+                               s_t_ld     <= '1';
+                               s_write    <= '1';
+                               s_ptr_op   <= ST_INC;
+                               s_ptr_ld   <= '1';
+
+                               -- Push PC onto the return stack
+                               r_sel      <= R_PC;
+                               r_write    <= '1';
+                               r_ptr_op   <= ST_INC;
+                               r_ptr_ld   <= '1';
+
+                               -- Load PC with the vector address
+                               pc_sel     <= PC_INTVEC;
+
+                               -- Mask further interruptions
+                               imask_set  <= '1';
+
+                               state_next <= S_FETCH;
+
+                       when S_RESET =>
+                               state_next <= S_FETCH;
+
+                       when others =>
+                               state_next <= S_FETCH;
+               end case;
+       end process;
+
+
+       -- Interrupt mask flag
+       process (rst_i, clk_i, imask_set, imask_clr)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' or imask_clr = '1' then
+                               imask_reg <= '0';
+                       elsif imask_set = '1' then
+                               imask_reg <= '1';
+                       end if;
+               end if;
+       end process;
+
+
+       -- Program counter
+       process (n, z, p, ins_decode)
+       begin
+               if ins_decode.cond = C_NONE then
+                       ins_pc_sel <= ins_decode.pc_sel;
+               elsif ins_decode.cond = C_NEG then
+                       if n = '1' then
+                               ins_pc_sel <= ins_decode.pc_sel;
+                       else
+                               ins_pc_sel <= PC_NC;
+                       end if;
+               elsif ins_decode.cond = C_ZERO then
+                       if z = '1' then
+                               ins_pc_sel <= ins_decode.pc_sel;
+                       else
+                               ins_pc_sel <= PC_NC;
+                       end if;
+               elsif ins_decode.cond = C_POS then
+                       if p = '1' then
+                               ins_pc_sel <= ins_decode.pc_sel;
+                       else
+                               ins_pc_sel <= PC_NC;
+                       end if;
+               end if;
+       end process;
+
+       with pc_sel select pc_next <=
+               mdr_reg                                when PC_MDR,
+               r_tos_reg                              when PC_R,
+               std_logic_vector(unsigned(pc_reg) + 1) when PC_INC,
+               (others => '0')                        when PC_INTVEC,
+               pc_reg                                 when others;
+
+       process (rst_i, clk_i, pc_reg, pc_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               pc_reg <= (others => '0');
+                       else
+                               pc_reg <= pc_next;
+                       end if;
+               end if;
+       end process;
+
+
+       -- Instruction register and decode
+       process (clk_i, ins_ld, dat_i)
+       begin
+               if rising_edge(clk_i) then
+                       if ins_ld = '1' then
+                               ins_reg <= dat_i;
+                       end if;
+               end if;
+       end process;
+
+       with ins_reg select ins_decode <=
+               (  -- #8
+                       mem_op => MEM_IMM8,
+                       s_op   => ST_INC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_MDR, t_to_n => '1',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00000001",
+               (  -- #32
+                       mem_op => MEM_IMM32,
+                       s_op   => ST_INC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_MDR, t_to_n => '1',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00000010",
+               (  -- @8
+                       mem_op => MEM_LD8,
+                       s_op   => ST_NC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_MDR, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00000011",
+               (  -- @32
+                       mem_op => MEM_LD32,
+                       s_op   => ST_NC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_MDR, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00000100",
+               (  -- !8
+                       mem_op => MEM_ST8,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_N, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00000101",
+               (  -- !32
+                       mem_op => MEM_ST32,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_N, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00000110",
+               (  -- r@
+                       mem_op => MEM_NONE,
+                       s_op   => ST_INC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_R, t_to_n => '1',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00000111",
+               (  -- >r
+                       mem_op => MEM_NONE,
+                       s_op   => ST_DEC,
+                       r_op   => ST_INC,
+                       alu_op => ALU_N, t_to_n => '0',
+                       r_sel  => R_T,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00001000",
+               (  -- r>
+                       mem_op => MEM_NONE,
+                       s_op   => ST_INC,
+                       r_op   => ST_DEC,
+                       alu_op => ALU_R, t_to_n => '1',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00001001",
+               (  -- call
+                       mem_op => MEM_IMM32,
+                       s_op   => ST_NC,
+                       r_op   => ST_INC,
+                       alu_op => ALU_T, t_to_n => '0',
+                       r_sel  => R_PC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00001010",
+               (  -- ;
+                       mem_op => MEM_NONE,
+                       s_op   => ST_NC,
+                       r_op   => ST_DEC,
+                       alu_op => ALU_T, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_R,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00001011",
+               (  -- jmp
+                       mem_op => MEM_IMM32,
+                       s_op   => ST_NC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_T, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00001100",
+               (  -- jz
+                       mem_op => MEM_IMM32,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_N, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_ZERO
+               ) when "00001101",
+               (  -- jn
+                       mem_op => MEM_IMM32,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_N, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NEG
+               ) when "00001110",
+               (  -- jp
+                       mem_op => MEM_IMM32,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_N, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_MDR,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_POS
+               ) when "00001111",
+               (  -- +
+                       mem_op => MEM_NONE,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_ADD, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00010000",
+               (  -- -
+                       mem_op => MEM_NONE,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_SUB, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00010001",
+               (  -- &
+                       mem_op => MEM_NONE,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_AND, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00010010",
+               (  -- |
+                       mem_op => MEM_NONE,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_OR, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00010011",
+               (  -- ^
+                       mem_op => MEM_NONE,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_XOR, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00010100",
+               (  -- ~
+                       mem_op => MEM_NONE,
+                       s_op   => ST_NC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_NOT, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00010101",
+               (  -- DUP
+                       mem_op => MEM_NONE,
+                       s_op   => ST_INC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_T, t_to_n => '1',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00010110",
+               (  -- DROP
+                       mem_op => MEM_NONE,
+                       s_op   => ST_DEC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_N, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00010111",
+               (  -- SWAP
+                       mem_op => MEM_NONE,
+                       s_op   => ST_NC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_N, t_to_n => '1',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00011000",
+               (  -- imask_set
+                       mem_op => MEM_NONE,
+                       s_op   => ST_NC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_T, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '1', imask_clr => '0',
+                       cond   => C_NONE
+               ) when "00011001",
+               (  -- imask_clr
+                       mem_op => MEM_NONE,
+                       s_op   => ST_NC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_T, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '1',
+                       cond   => C_NONE
+               ) when "00011010",
+               (  -- NOP
+                       mem_op => MEM_NONE,
+                       s_op   => ST_NC,
+                       r_op   => ST_NC,
+                       alu_op => ALU_T, t_to_n => '0',
+                       r_sel  => R_NC,
+                       pc_sel => PC_NC,
+                       imask_set => '0', imask_clr => '0',
+                       cond   => C_NONE
+               ) when others;
+
+
+       -- Address multiplexer
+       with adr_sel select adr_o <=
+               pc_reg                      when ADR_PC,
+               s_t_reg                     when ADR_T,
+               s_t_reg(31 downto 2) & "00" when ADR_T0,
+               s_t_reg(31 downto 2) & "01" when ADR_T1,
+               s_t_reg(31 downto 2) & "10" when ADR_T2,
+               s_t_reg(31 downto 2) & "11" when ADR_T3,
+               (others => '0')             when others;
+
+
+       -- Memory data register
+       process (clk_i, mdr_ld_0, mdr_ld_1, mdr_ld_2, mdr_ld_3)
+       begin
+               if rising_edge(clk_i) then
+                       if mdr_ld_0 = '1' then
+                               -- Zero-extend for 8-bit loads
+                               -- Zeroes will be overwritten for 32-bit loads
+                               mdr_reg( 7 downto  0) <= dat_i;
+                               mdr_reg(31 downto  8) <= (others => '0');
+                       elsif mdr_ld_1 = '1' then
+                               mdr_reg(15 downto  8) <= dat_i;
+                       elsif mdr_ld_2 = '1' then
+                               mdr_reg(23 downto 16) <= dat_i;
+                       elsif mdr_ld_3 = '1' then
+                               mdr_reg(31 downto 24) <= dat_i;
+                       end if;
+               end if;
+       end process;
+
+       with dat_sel select dat_o <=
+               s_n_reg(31 downto 24) when DAT_N3,
+               s_n_reg(23 downto 16) when DAT_N2,
+               s_n_reg(15 downto  8) when DAT_N1,
+               s_n_reg( 7 downto  0) when others;
+
+
+       -- ALU
+       with alu_op select s_t_new <=
+               s_t_reg             when ALU_T,
+                           s_n_reg when ALU_N,
+               std_logic_vector(signed(s_t_reg) + signed(s_n_reg)) when ALU_ADD,
+               std_logic_vector(signed(s_t_reg) - signed(s_n_reg)) when ALU_SUB,
+               s_t_reg and s_n_reg when ALU_AND,
+               s_t_reg or  s_n_reg when ALU_OR,
+               s_t_reg xor s_n_reg when ALU_XOR,
+               not s_t_reg         when ALU_NOT,
+               r_tos_reg           when ALU_R,
+               mdr_reg             when ALU_MDR,
+               x"000000" & vec_i   when ALU_INTVEC,
+               (others => '0')     when others;
+
+
+       -- Next R
+       with r_sel select r_tos_new <=
+               s_t_reg   when R_T,
+               pc_reg    when R_PC,
+               r_tos_reg when others;
+
+
+       -- Stack memory
+       p_s_tos: process (clk_i, s_t_ld, s_t_new)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               -- Reset to zero as the "reset vector"
+                               s_t_reg <= (others => '0');
+                       elsif s_t_ld = '1' then
+                               s_t_reg <= s_t_new;
+                       end if;
+               end if;
+       end process;
+
+       n <= s_t_reg(s_t_reg'high);
+       z <= not or_reduce(s_t_reg);
+       p <= (not n) and (not z);
+
+       p_s_ptr: process (rst_i, clk_i, s_ptr_ld, s_ptr_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               s_ptr_reg <= (others => '0');
+                       else
+                               if s_ptr_ld = '1' then
+                                       s_ptr_reg <= s_ptr_next;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       with s_ptr_op select s_ptr_next <=
+               s_ptr_reg + 1 when ST_INC,
+               s_ptr_reg - 1 when ST_DEC,
+               s_ptr_reg     when others;
+
+       p_r_ptr: process (rst_i, clk_i, r_ptr_ld, r_ptr_next)
+       begin
+               if rising_edge(clk_i) then
+                       if rst_i = '1' then
+                               r_ptr_reg <= (others => '0');
+                       else
+                               if r_ptr_ld = '1' then
+                                       r_ptr_reg <= r_ptr_next;
+                               end if;
+                       end if;
+               end if;
+       end process;
+
+       with r_ptr_op select r_ptr_next <=
+               r_ptr_reg + 1 when ST_INC,
+               r_ptr_reg - 1 when ST_DEC,
+               r_ptr_reg     when others;
+
+       addra <= '0' & std_logic_vector(s_ptr_next);
+       addrb <= '1' & std_logic_vector(r_ptr_next);
+
+       e_stack_mem: ramb16_s36_s36
+               port map (
+                       -- Port A, parameter stack
+                       ssra  => rst_i,
+                       clka  => clk_i,
+                       ena   => '1',
+                       wea   => s_write,
+                       addra => addra,
+
+                       doa   => s_n_reg,
+                       dopa  => open,
+                       dia   => s_t_reg,
+                       dipa  => "0000",
+
+                       -- Port B, return stack
+                       ssrb  => rst_i,
+                       clkb  => clk_i,
+                       enb   => '1',
+                       web   => r_write,
+                       addrb => addrb,
+
+                       dob   => r_tos_reg,
+                       dopb  => open,
+                       dib   => r_tos_new,
+                       dipb  => "0000"
+               );
+
+
+end behavioral;
diff --git a/projects/cpu_0/int_ctrl.vhd b/projects/cpu_0/int_ctrl.vhd
new file mode 100644 (file)
index 0000000..72b52de
--- /dev/null
@@ -0,0 +1,33 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+use ieee.numeric_std.all;
+
+
+entity int_ctrl is
+       port (
+               int_o: out std_logic;
+               vec_o: out std_logic_vector(3 downto 0);
+
+               int_i: in  std_logic_vector(15 downto 0)
+       );
+end int_ctrl;
+
+
+architecture behavioral of int_ctrl is
+begin
+
+       int_o <= or_reduce(int_i);
+
+       process (int_i)
+       begin
+               vec_o <= (others => '0');
+
+               for i in 15 downto 0 loop
+                       if int_i(i) = '1' then
+                               vec_o <= std_logic_vector(to_unsigned(i, vec_o'length));
+                       end if;
+               end loop;
+       end process;
+
+end behavioral;
diff --git a/projects/cpu_0/nexys2.vhd b/projects/cpu_0/nexys2.vhd
new file mode 100644 (file)
index 0000000..c2f156d
--- /dev/null
@@ -0,0 +1,527 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_misc.all;
+
+library utility;
+library nexys2_lib;
+library ps2;
+library rs232;
+library vga;
+
+
+entity nexys2 is
+       port (
+               -- Clock input
+               clk_50:     in    std_logic;
+
+               -- EPP interface
+               DB:         inout std_logic_vector(7 downto 0);
+               EppWRITE:   in    std_logic;
+               EppASTB:    in    std_logic;
+               EppDSTB:    in    std_logic;
+               EppWAIT:    out   std_logic;
+
+               -- Memory interface
+               MemOE:      out   std_logic;
+               MemWR:      out   std_logic;
+               RamAdv:     out   std_logic;
+               RamCS:      out   std_logic;
+               RamClk:     out   std_logic;
+               RamCRE:     out   std_logic;
+               RamLB:      out   std_logic;
+               RamUB:      out   std_logic;
+               RamWait:    in    std_logic;
+               FlashRp:    out   std_logic;
+               FlashCS:    out   std_logic;
+               FlashStSts: in    std_logic;
+               MemAdr:     out   std_logic_vector(23 downto 1);
+               MemDB:      inout std_logic_vector(15 downto 0);
+
+               -- Switches and LEDs
+               seg:        out   std_logic_vector(6 downto 0);
+               dp:         out   std_logic;
+               an:         out   std_logic_vector(3 downto 0);
+               Led:        out   std_logic_vector(7 downto 0);
+               sw:         in    std_logic_vector(7 downto 0);
+               btn:        in    std_logic_vector(3 downto 0);
+
+               -- VGA video output
+               vgaRed:     out   std_logic_vector(3 downto 1);
+               vgaGreen:   out   std_logic_vector(3 downto 1);
+               vgaBlue:    out   std_logic_vector(3 downto 2);
+               Hsync:      out   std_logic;
+               Vsync:      out   std_logic;
+
+               -- PS2 (keyboard)
+               PS2C:       inout std_logic;
+               PS2D:       inout std_logic;
+
+               -- RS232 (mouse)
+               RsRx:       in    std_logic;
+               RsTx:       out   std_logic
+       );
+end nexys2;
+
+
+architecture behavioral of nexys2 is
+
+       -- Device Wishbone SYSCON
+       signal g_rst:             std_logic;  -- Global power-on-reset
+       signal h_rst:             std_logic;  -- Host controller reset
+       signal d_rst:             std_logic;  -- Device reset (P.O.R. or host)
+       signal d_clk:             std_logic;  -- Device clock, in case DCM used instead of clk_50 later
+
+       -- Wishbone busses
+       signal cyc:               std_logic;
+       signal stb:               std_logic;
+       signal we:                std_logic;
+       signal ack:               std_logic;
+       signal adr:               std_logic_vector(31 downto 0);
+       signal dat_mosi:          std_logic_vector(7 downto 0);
+       signal dat_miso:          std_logic_vector(7 downto 0);
+
+       signal mem_cyc:           std_logic;
+       signal mem_stb:           std_logic;
+       signal mem_we:            std_logic;
+       signal mem_ack:           std_logic;
+       signal mem_adr:           std_logic_vector(31 downto 0);
+       signal mem_mosi:          std_logic_vector(7 downto 0);
+       signal mem_miso:          std_logic_vector(7 downto 0);
+
+       signal tile_cyc:          std_logic;
+       signal tile_stb:          std_logic;
+       signal tile_we:           std_logic;
+       signal tile_ack:          std_logic;
+       signal tile_adr:          std_logic_vector(31 downto 0);
+       signal tile_mosi:         std_logic_vector(7 downto 0);
+       signal tile_miso:         std_logic_vector(7 downto 0);
+
+       signal host_cyc:          std_logic;
+       signal host_stb:          std_logic;
+       signal host_we:           std_logic;
+       signal host_ack:          std_logic;
+       signal host_adr:          std_logic_vector(31 downto 0);
+       signal host_mosi:         std_logic_vector(7 downto 0);
+       signal host_miso:         std_logic_vector(7 downto 0);
+
+       signal ps2_cyc:           std_logic;
+       signal ps2_stb:           std_logic;
+       signal ps2_we:            std_logic;
+       signal ps2_ack:           std_logic;
+       signal ps2_adr:           std_logic_vector(31 downto 0);
+       signal ps2_mosi:          std_logic_vector(7 downto 0);
+       signal ps2_miso:          std_logic_vector(7 downto 0);
+
+       signal uart_cyc:          std_logic;
+       signal uart_stb:          std_logic;
+       signal uart_we:           std_logic;
+       signal uart_ack:          std_logic;
+       signal uart_adr:          std_logic_vector(31 downto 0);
+       signal uart_mosi:         std_logic_vector(7 downto 0);
+       signal uart_miso:         std_logic_vector(7 downto 0);
+
+       -- Interrupt signals
+       signal host_flags:        std_logic_vector(7 downto 0);
+
+       signal ps2_rx_ready:      std_logic;
+       signal ps2_rx_full:       std_logic;
+       signal ps2_tx_ready:      std_logic;
+       signal ps2_tx_empty:      std_logic;
+       signal ps2_err_nack:      std_logic;
+       signal ps2_err_txto:      std_logic;
+       signal ps2_err_rxto:      std_logic;
+       signal ps2_err_missed:    std_logic;
+       signal ps2_err_parity:    std_logic;
+       signal ps2_err_framing:   std_logic;
+
+       signal uart_rx_ready:     std_logic;
+       signal uart_rx_full:      std_logic;
+       signal uart_tx_ready:     std_logic;
+       signal uart_tx_empty:     std_logic;
+       signal uart_err_break:    std_logic;
+       signal uart_err_framing:  std_logic;
+       signal uart_err_parity:   std_logic;
+       signal uart_err_overflow: std_logic;
+
+       -- Memory physical interface, routed through host controller
+       signal d_MemOE:           std_logic;
+       signal d_MemWR:           std_logic;
+       signal d_RamAdv:          std_logic;
+       signal d_RamCS:           std_logic;
+       signal d_RamClk:          std_logic;
+       signal d_RamCRE:          std_logic;
+       signal d_RamUB:           std_logic;
+       signal d_RamLB:           std_logic;
+       signal d_RamWait:         std_logic;
+       signal d_FlashRp:         std_logic;
+       signal d_FlashCS:         std_logic;
+       signal d_FlashStSts:      std_logic;
+       signal d_MemAdr:          std_logic_vector(23 downto 1);
+       signal d_MemDB_i:         std_logic_vector(15 downto 0);
+       signal d_MemDB_o:         std_logic_vector(15 downto 0);
+
+       -- Interrupt signals
+       signal int_cpu:           std_logic;
+       signal int_vec:           std_logic_vector(7 downto 0);
+       signal ints:              std_logic_vector(15 downto 0);
+
+       signal debug_i:           std_logic_vector(63 downto 0);
+       signal debug_o:           std_logic_vector(63 downto 0);
+
+begin
+
+       Led <= mem_miso;
+       e_cpu: entity work.cpu
+               port map (
+                       rst_i => d_rst,
+                       clk_i => d_clk,
+
+                       cyc_o => cyc,
+                       stb_o => stb,
+                       we_o  => we,
+                       ack_i => ack,
+                       adr_o => adr,
+                       dat_o => dat_mosi,
+                       dat_i => dat_miso,
+
+                       int_i => int_cpu,
+                       vec_i => int_vec,
+
+                       debug_i => debug_o,
+                       debug_o => debug_i
+               );
+
+       int_vec(7 downto 4) <= (others => '0');
+       ints(0) <= '0';
+       e_int: entity work.int_ctrl
+               port map (
+                       int_o => int_cpu,
+                       vec_o => int_vec(3 downto 0),
+                       int_i => ints
+               );
+
+       d_clk <= clk_50;
+
+       e_por: entity utility.power_on_reset
+               port map (
+                       rst_i => '0',
+                       clk_i => d_clk,
+                       rst_o => g_rst
+               );
+
+       d_rst <= g_rst or h_rst;
+
+       -- Maybe it would be more understandable if the mapper just produced select
+       -- signals from mask/match/address inputs?  Then each device can and the
+       -- global cyc signal with its own select signal, take the rest of the bus
+       -- signals as they are, produce their own miso+ack, then have a separate mux
+       -- for misos+acks?
+       e_mapper: entity utility.wb_mapper
+               generic map (
+                       A_WIDTH => 32,
+                       D_WIDTH => 8
+               )
+               port map (
+                       cyc_i   => cyc,
+                       stb_i   => stb,
+                       we_i    => we,
+                       ack_o   => ack,
+                       adr_i   => adr,
+                       dat_i   => dat_mosi,
+                       dat_o   => dat_miso,
+
+                       -- Flash: 0x00000000-0x00ffffff
+                       -- Ram:   0x01000000-0x01ffffff
+                       mask_0  => "00000010000000000000000000000000",
+                       match_0 => "00000000000000000000000000000000",
+                       cyc_o_0 => mem_cyc,
+                       stb_o_0 => mem_stb,
+                       we_o_0  => mem_we,
+                       ack_i_0 => mem_ack,
+                       adr_o_0 => mem_adr,
+                       dat_o_0 => mem_mosi,
+                       dat_i_0 => mem_miso,
+
+                       -- Vbuf:  0x02000000-0x02001fff
+                       -- Tiles: 0x02002000-0x02003fff
+                       mask_1  => "00000010000000000100000000000000",
+                       match_1 => "00000010000000000000000000000000",
+                       cyc_o_1 => tile_cyc,
+                       stb_o_1 => tile_stb,
+                       we_o_1  => tile_we,
+                       ack_i_1 => tile_ack,
+                       adr_o_1 => tile_adr,
+                       dat_o_1 => tile_mosi,
+                       dat_i_1 => tile_miso,
+
+                       -- Host:  0x02004000-0x02004007
+                       mask_2  => "00000010000000000100000000011000",
+                       match_2 => "00000010000000000100000000000000",
+                       cyc_o_2 => host_cyc,
+                       stb_o_2 => host_stb,
+                       we_o_2  => host_we,
+                       ack_i_2 => host_ack,
+                       adr_o_2 => host_adr,
+                       dat_o_2 => host_mosi,
+                       dat_i_2 => host_miso,
+
+                       -- PS2:   0x02004008-0x0200400f
+                       mask_3  => "00000010000000000100000000011000",
+                       match_3 => "00000010000000000100000000001000",
+                       cyc_o_3 => ps2_cyc,
+                       stb_o_3 => ps2_stb,
+                       we_o_3  => ps2_we,
+                       ack_i_3 => ps2_ack,
+                       adr_o_3 => ps2_adr,
+                       dat_o_3 => ps2_mosi,
+                       dat_i_3 => ps2_miso,
+
+                       -- UART:  0x02004010-0x02004017
+                       mask_4  => "00000010000000000100000000011000",
+                       match_4 => "00000010000000000100000000010000",
+                       cyc_o_4 => uart_cyc,
+                       stb_o_4 => uart_stb,
+                       we_o_4  => uart_we,
+                       ack_i_4 => uart_ack,
+                       adr_o_4 => uart_adr,
+                       dat_o_4 => uart_mosi,
+                       dat_i_4 => uart_miso,
+
+                       mask_5  => "00000000000000000000000000000000",
+                       match_5 => "00000000000000000000000000000001",
+                       cyc_o_5 => open,
+                       stb_o_5 => open,
+                       we_o_5  => open,
+                       ack_i_5 => '1',
+                       adr_o_5 => open,
+                       dat_o_5 => open,
+                       dat_i_5 => "00000000",
+
+                       mask_6  => "00000000000000000000000000000000",
+                       match_6 => "00000000000000000000000000000001",
+                       cyc_o_6 => open,
+                       stb_o_6 => open,
+                       we_o_6  => open,
+                       ack_i_6 => '1',
+                       adr_o_6 => open,
+                       dat_o_6 => open,
+                       dat_i_6 => "00000000",
+
+                       mask_7  => "00000000000000000000000000000000",
+                       match_7 => "00000000000000000000000000000001",
+                       cyc_o_7 => open,
+                       stb_o_7 => open,
+                       we_o_7  => open,
+                       ack_i_7 => '1',
+                       adr_o_7 => open,
+                       dat_o_7 => open,
+                       dat_i_7 => "00000000"
+               );
+
+
+       e_mem: entity nexys2_lib.mem_wb8_0
+               port map (
+                       rst_i       => d_rst,
+                       clk_i       => d_clk,
+
+                       -- Internal access
+                       cyc_i       => mem_cyc,
+                       stb_i       => mem_stb,
+                       we_i        => mem_we,
+                       ack_o       => mem_ack,
+                       adr_i       => mem_adr(24 downto 0),
+                       dat_i       => mem_mosi,
+                       dat_o       => mem_miso,
+
+                       -- Configuration
+                       wait_cycles => "01100",
+
+                       -- Memory interface
+                       MemOE       => d_MemOE,
+                       MemWR       => d_MemWR,
+                       RamAdv      => d_RamAdv,
+                       RamCS       => d_RamCS,
+                       RamClk      => d_RamClk,
+                       RamCRE      => d_RamCRE,
+                       RamUB       => d_RamUB,
+                       RamLB       => d_RamLB,
+                       RamWait     => d_RamWait,
+                       FlashRp     => d_FlashRp,
+                       FlashCS     => d_FlashCS,
+                       FlashStSts  => d_FlashStSts,
+                       MemAdr      => d_MemAdr,
+                       MemDB_i     => d_MemDB_i,
+                       MemDB_o     => d_MemDB_o
+               );
+
+
+       e_vga: entity vga.vga_tiler
+               port map (
+                       rst_i    => d_rst,
+                       clk_i    => d_clk,
+
+                       -- Internal access to screen buffer and tile set
+                       cyc_i    => tile_cyc,
+                       stb_i    => tile_stb,
+                       we_i     => tile_we,
+                       ack_o    => tile_ack,
+                       adr_i    => tile_adr(13 downto 0),
+                       dat_i    => tile_mosi,
+                       dat_o    => tile_miso,
+
+                       -- External pins
+                       vgaRed   => vgaRed,
+                       vgaGreen => vgaGreen,
+                       vgaBlue  => vgaBlue,
+                       Hsync    => Hsync,
+                       Vsync    => Vsync
+               );
+
+
+       ints(1) <= or_reduce(host_flags);
+       e_host: entity nexys2_lib.host_ctrl
+               port map (
+                       clk_i        => d_clk,
+                       rst_i        => g_rst,
+
+                       -- Signals to the internal device
+                       d_rst_o      => h_rst,
+                       d_flags_o    => host_flags,
+                       debug_i      => debug_i,
+                       debug_o      => debug_o,
+
+                       -- Internal access to control registers
+                       d_cyc_i      => host_cyc,
+                       d_stb_i      => host_stb,
+                       d_we_i       => host_we,
+                       d_ack_o      => host_ack,
+                       d_adr_i      => host_adr(2 downto 0),
+                       d_dat_i      => host_mosi,
+                       d_dat_o      => host_miso,
+
+                       -- Internal memory interface, can be switched off to allow the host to control memory
+                       d_MemOE      => d_MemOE,
+                       d_MemWR      => d_MemWR,
+                       d_RamAdv     => d_RamAdv,
+                       d_RamCS      => d_RamCS,
+                       d_RamClk     => d_RamClk,
+                       d_RamCRE     => d_RamCRE,
+                       d_RamLB      => d_RamLB,
+                       d_RamUB      => d_RamUB,
+                       d_RamWait    => d_RamWait,
+                       d_FlashRp    => d_FlashRp,
+                       d_FlashCS    => d_FlashCS,
+                       d_FlashStSts => d_FlashStSts,
+                       d_MemAdr     => d_MemAdr,
+                       d_MemDB_o    => d_MemDB_o,
+                       d_MemDB_i    => d_MemDB_i,
+
+                       -- External pins
+                       EppAstb      => EppASTB,
+                       EppDstb      => EppDSTB,
+                       EppWr        => EppWRITE,
+                       EppDB        => DB,
+                       EppWait      => EppWAIT,
+
+                       MemOE        => MemOE,
+                       MemWR        => MemWR,
+                       RamAdv       => RamAdv,
+                       RamCS        => RamCS,
+                       RamClk       => RamClk,
+                       RamCRE       => RamCRE,
+                       RamLB        => RamLB,
+                       RamUB        => RamUB,
+                       RamWait      => RamWait,
+                       FlashRp      => FlashRp,
+                       FlashCS      => FlashCS,
+                       FlashStSts   => FlashStSts,
+                       MemAdr       => MemAdr,
+                       MemDB        => MemDB,
+
+                       seg          => seg,
+                       dp           => dp,
+                       an           => an,
+                       Led          => open,
+                       sw           => sw
+               );
+
+
+       ints(2) <= ps2_rx_ready    or
+                  ps2_rx_full     or
+                  ps2_tx_ready    or
+                  ps2_tx_empty    or
+                  ps2_err_nack    or
+                  ps2_err_txto    or
+                  ps2_err_rxto    or
+                  ps2_err_missed  or
+                  ps2_err_parity  or
+                  ps2_err_framing;
+       e_ps2: entity ps2.ps2_host_wb
+               port map (
+                       rst_i       => d_rst,
+                       clk_i       => d_clk,
+
+                       -- Internal access
+                       cyc_i       => ps2_cyc,
+                       stb_i       => ps2_stb,
+                       we_i        => ps2_we,
+                       ack_o       => ps2_ack,
+                       adr_i       => ps2_adr(2 downto 0),
+                       dat_i       => ps2_mosi,
+                       dat_o       => ps2_miso,
+
+                       -- Interrupt signals
+                       rx_ready    => ps2_rx_ready,
+                       rx_full     => ps2_rx_full,
+                       tx_ready    => ps2_tx_ready,
+                       tx_empty    => ps2_tx_empty,
+                       err_nack    => ps2_err_nack,
+                       err_txto    => ps2_err_txto,
+                       err_rxto    => ps2_err_rxto,
+                       err_missed  => ps2_err_missed,
+                       err_parity  => ps2_err_parity,
+                       err_framing => ps2_err_framing,
+
+                       -- External pins
+                       ps2_clk     => PS2C,
+                       ps2_data    => PS2D
+               );
+
+
+       ints(3) <= uart_rx_ready     or
+                  uart_rx_full      or
+                  uart_tx_ready     or
+                  uart_tx_empty     or
+                  uart_err_break    or
+                  uart_err_framing  or
+                  uart_err_overflow;
+       e_rs232: entity rs232.rs232_uart
+               port map (
+                       rst_i        => d_rst,
+                       clk_i        => d_clk,
+
+                       -- Internal access
+                       cyc_i        => uart_cyc,
+                       stb_i        => uart_stb,
+                       we_i         => uart_we,
+                       ack_o        => uart_ack,
+                       adr_i        => uart_adr(2 downto 0),
+                       dat_i        => uart_mosi,
+                       dat_o        => uart_miso,
+
+                       -- Interrupt signals
+                       rx_ready     => uart_rx_ready,
+                       rx_full      => uart_rx_full,
+                       tx_ready     => uart_tx_ready,
+                       tx_empty     => uart_tx_empty,
+                       err_break    => uart_err_break,
+                       err_framing  => uart_err_framing,
+                       err_parity   => uart_err_parity,
+                       err_overflow => uart_err_overflow,
+
+                       -- External pins
+                       tx           => RsTx,
+                       rx           => RsRx
+               );
+
+end behavioral;
diff --git a/projects/cpu_0/program.sh b/projects/cpu_0/program.sh
new file mode 100755 (executable)
index 0000000..ac51e3b
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog
+
diff --git a/projects/cpu_0/tests/test_cpu.vhd b/projects/cpu_0/tests/test_cpu.vhd
new file mode 100644 (file)
index 0000000..183cc3f
--- /dev/null
@@ -0,0 +1,111 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library simulated;
+library work;
+
+
+entity test_cpu is
+end test_cpu;
+
+
+architecture behavior of test_cpu is
+
+       constant clk_i_period: time := 10 ns;
+
+       signal rst_i: std_logic;
+       signal clk_i: std_logic;
+
+       signal cyc_o: std_logic;
+       signal stb_o: std_logic;
+       signal we_o:  std_logic;
+       signal ack_i: std_logic;
+       signal adr_o: std_logic_vector(31 downto 0);
+       signal dat_o: std_logic_vector(7 downto 0);
+       signal dat_i: std_logic_vector(7 downto 0);
+
+       signal int_i: std_logic;
+       signal vec_i: std_logic_vector(7 downto 0);
+
+begin
+
+       p_test: process
+       begin
+               -- Initial values
+               int_i <= '0';
+               vec_i <= x"00";
+
+               -- Reset
+               rst_i <= '1';
+               wait for clk_i_period * 2;
+               rst_i <= '0';
+
+               -- Fire interrupt
+               wait for 200 ns;
+               vec_i <= x"c3";
+               int_i <= '1';
+
+               -- Done
+               wait;
+       end process;
+
+
+       e_mem: entity simulated.wb_memory
+               generic map (
+                       -- Test param stack, some math, and store
+                       -- #0x24 #0x65 #0x03 #0x05 - + #0xdeadbeef !
+                       -- INIT => x"0124016501030105111002deadbeef0600000000000000000000000000000000"
+
+                       -- Test pushes and pops of both stacks
+                       -- #0x11 #0x22 #0x33 #0x44 #0x55 #0x66 >R >R >R >R >R >R R> R> R> R> R> R>
+                       -- INIT => x"0111012201330144015501660808080808080909090909090000000000000000"
+
+                       -- Test call, jmp and return
+                       -- call 0x10 jmp 0x5
+                       -- INIT => x"0a100000000c05000000000000000000015a0b00000000000000000000000000"
+
+                       -- Test conditional branch and interrupts
+                       -- 0x00: jz 0x10 #0xa5 ;    0x10: jmp 0x10
+                       INIT => x"0d1000000001a50b00000000000000000c100000000000000000000000000000"
+               )
+               port map (
+                       rst_i => rst_i,
+                       clk_i => clk_i,
+
+                       cyc_i => cyc_o,
+                       stb_i => stb_o,
+                       we_i  => we_o,
+                       ack_o => ack_i,
+                       adr_i => adr_o(4 downto 0),
+                       dat_i => dat_o,
+                       dat_o => dat_i
+               );
+
+
+       e_uut: entity work.cpu
+               port map (
+                       rst_i => rst_i,
+                       clk_i => clk_i,
+
+                       cyc_o => cyc_o,
+                       stb_o => stb_o,
+                       we_o  => we_o,
+                       ack_i => ack_i,
+                       adr_o => adr_o,
+                       dat_o => dat_o,
+                       dat_i => dat_i,
+
+                       int_i => int_i,
+                       vec_i => vec_i
+               );
+
+
+       p_clk: process
+       begin
+               clk_i <= '0';
+               wait for clk_i_period/2;
+               clk_i <= '1';
+               wait for clk_i_period/2;
+       end process;
+
+end;
diff --git a/projects/cpu_0/tests/test_intctrl.vhd b/projects/cpu_0/tests/test_intctrl.vhd
new file mode 100644 (file)
index 0000000..623f6b9
--- /dev/null
@@ -0,0 +1,56 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+
+entity test_intctrl is
+end test_intctrl;
+
+
+architecture behavior of test_intctrl is
+
+       signal int_i: std_logic_vector(15 downto 0);
+       signal int_o : std_logic;
+       signal vec_o : std_logic_vector(3 downto 0);
+
+begin
+
+       p_test: process
+       begin
+               -- Initial values
+               int_i <= "0000000000000000";
+               wait for 1 ns;
+               assert int_o = '0';
+
+               wait for 10 ns;
+
+               int_i <= "0000000000001000";
+               wait for 1 ns;
+               assert int_o = '1';
+               assert vec_o = x"3";
+
+               wait for 10 ns;
+
+               int_i <= "0001000000001000";
+               wait for 1 ns;
+               assert int_o = '1';
+               assert vec_o = x"3";
+
+               wait for 10 ns;
+
+               int_i <= "0001000000000000";
+               wait for 1 ns;
+               assert int_o = '1';
+               assert vec_o = x"c";
+
+               -- Done
+               wait;
+       end process;
+
+       e_uut: entity work.int_ctrl
+               port map (
+                       int_o => int_o,
+                       vec_o => vec_o,
+                       int_i => int_i
+               );
+
+end;
diff --git a/projects/cpu_0/tests/test_nexys2.vhd b/projects/cpu_0/tests/test_nexys2.vhd
new file mode 100644 (file)
index 0000000..d8010b2
--- /dev/null
@@ -0,0 +1,162 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library simulated;
+library work;
+
+
+entity test_nexys2 is
+end test_nexys2;
+
+
+architecture behavior of test_nexys2 is
+
+       constant clk_50_period: time := 20 ns;
+
+       signal clk_50:     std_logic;
+
+       signal EppWRITE:   std_logic;
+       signal EppASTB:    std_logic;
+       signal EppDSTB:    std_logic;
+       signal DB:         std_logic_vector(7 downto 0);
+       signal EppWAIT:    std_logic;
+
+       signal MemOE:      std_logic;
+       signal MemWR:      std_logic;
+       signal RamAdv:     std_logic;
+       signal RamCS:      std_logic;
+       signal RamClk:     std_logic;
+       signal RamCRE:     std_logic;
+       signal RamLB:      std_logic;
+       signal RamUB:      std_logic;
+       signal RamWait:    std_logic;
+       signal FlashRp:    std_logic;
+       signal FlashCS:    std_logic;
+       signal FlashStSts: std_logic;
+       signal MemAdr:     std_logic_vector(23 downto 1);
+       signal MemDB:      std_logic_vector(15 downto 0);
+
+       signal vgaRed:     std_logic_vector(3 downto 1);
+       signal vgaGreen:   std_logic_vector(3 downto 1);
+       signal vgaBlue:    std_logic_vector(3 downto 2);
+       signal Hsync:      std_logic;
+       signal Vsync:      std_logic;
+
+       signal sw:         std_logic_vector(7 downto 0);
+       signal btn:        std_logic_vector(3 downto 0);
+       signal seg:        std_logic_vector(6 downto 0);
+       signal dp:         std_logic;
+       signal an:         std_logic_vector(3 downto 0);
+       signal Led:        std_logic_vector(7 downto 0);
+
+       signal PS2C:       std_logic;
+       signal PS2D:       std_logic;
+
+       signal RsRx:       std_logic;
+       signal RsTx:       std_logic;
+
+       signal flash_ce:   std_logic_vector(2 downto 0);
+       signal flash_adr:  std_logic_vector(23 downto 0);
+
+begin
+
+       p_test: process
+       begin
+               -- Initial values
+               EppWRITE <= '1';
+               EppASTB  <= '1';
+               EppDSTB  <= '1';
+               DB       <= (others => 'Z');
+               sw       <= (others => '0');
+               btn      <= (others => '0');
+               PS2C     <= 'H';
+               PS2D     <= 'H';
+               RsRx     <= '0';
+
+               -- Done
+               wait;
+       end process;
+
+
+       flash_ce <= "00" & FlashCS;
+       flash_adr <= MemAdr & '0';
+       e_flash: entity simulated.js28f128j3d75
+               port map (
+                       a      => flash_adr,
+                       d      => MemDB,
+                       ce     => flash_ce,
+                       rp_n   => FlashRp,
+                       oe_n   => MemOE,
+                       we_n   => MemWR,
+                       sts    => FlashStSts,
+                       byte_n => '1',
+                       vpen   => '1'
+               );
+
+
+       e_ram: entity simulated.mt45w8mw16bgx
+               port map (
+                       a     => MemAdr,
+                       dq    => MemDB,
+                       clk   => RamClk,
+                       adv_n => RamAdv,
+                       cre   => RamCRE,
+                       ce_n  => RamCS,
+                       oe_n  => MemOE,
+                       we_n  => MemWR,
+                       lb_n  => RamLB,
+                       ub_n  => RamUB,
+                       rwait => RamWait
+               );
+
+
+       e_uut: entity work.nexys2
+               port map (
+                       clk_50     => clk_50,
+                       DB         => DB,
+                       EppWRITE   => EppWRITE,
+                       EppASTB    => EppASTB,
+                       EppDSTB    => EppDSTB,
+                       EppWAIT    => EppWAIT,
+                       MemOE      => MemOE,
+                       MemWR      => MemWR,
+                       RamAdv     => RamAdv,
+                       RamCS      => RamCS,
+                       RamClk     => RamClk,
+                       RamCRE     => RamCRE,
+                       RamLB      => RamLB,
+                       RamUB      => RamUB,
+                       RamWait    => RamWait,
+                       FlashRp    => FlashRp,
+                       FlashCS    => FlashCS,
+                       FlashStSts => FlashStSts,
+                       MemAdr     => MemAdr,
+                       MemDB      => MemDB,
+                       seg        => seg,
+                       dp         => dp,
+                       an         => an,
+                       Led        => Led,
+                       sw         => sw,
+                       btn        => btn,
+                       vgaRed     => vgaRed,
+                       vgaGreen   => vgaGreen,
+                       vgaBlue    => vgaBlue,
+                       Hsync      => Hsync,
+                       Vsync      => Vsync,
+                       PS2C       => PS2C,
+                       PS2D       => PS2D,
+                       RsRx       => RsRx,
+                       RsTx       => RsTx
+               );
+
+
+       p_clk_50: process
+       begin
+               clk_50 <= '0';
+               wait for clk_50_period/2;
+               clk_50 <= '1';
+               wait for clk_50_period/2;
+       end process;
+
+end;
diff --git a/projects/nexys2_host_controller/device/program.sh b/projects/nexys2_host_controller/device/program.sh
new file mode 100755 (executable)
index 0000000..14d6689
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog
+#djtgcfg -d Nexys2 -i 0 -f OnBoardMemCfg.bit prog
+
diff --git a/projects/nexys2_host_controller/host/build.sh b/projects/nexys2_host_controller/host/build.sh
new file mode 100755 (executable)
index 0000000..dcc8e1e
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+INC=/usr/include/digilent/adept
+LIBDIR=/usr/lib64/digilent/adept
+
+CFLAGS="-std=c2x -Wall -Wextra -Wpedantic -D_POSIX_C_SOURCE"
+
+gcc -o epp_read  epp_read.c  ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr
+gcc -o epp_write epp_write.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr
+gcc -o digdude digdude.c fileformat.c memctrl.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr
+
diff --git a/projects/nexys2_host_controller/host/digdude.c b/projects/nexys2_host_controller/host/digdude.c
new file mode 100644 (file)
index 0000000..135d73b
--- /dev/null
@@ -0,0 +1,498 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <assert.h>
+
+#include "fileformat.h"
+#include "memctrl.h"
+#include "ec.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+
+// -----------------------------------------------------------------------------
+
+
+enum mem { RAM, FLASH, CFI };
+enum mode { M_READ, M_WRITE, M_VERIFY };
+enum exitspec { X_RESET, X_NORESET };
+
+
+// -----------------------------------------------------------------------------
+
+
+bool update_ram_read(HIF hif, struct iter *iter, uint32_t addr, size_t data_len)
+{
+       assert(hif != hifInvalid);
+       assert(iter != NULL);
+
+       uint8_t chunk[MAX_CHUNK];
+       size_t bytes_read = 0;
+
+       while (bytes_read < data_len)
+       {
+               size_t num_cur_bytes = MIN(data_len - bytes_read, MAX_CHUNK);
+
+               EC_FALSE(ram_read(hif, addr, chunk, num_cur_bytes));
+               EC_FALSE(iter_append(iter, addr, chunk, num_cur_bytes));
+
+               addr += num_cur_bytes;
+               bytes_read += num_cur_bytes;
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool update_ram_write(HIF hif, struct iter *iter, bool verify)
+{
+       assert(hif != hifInvalid);
+       assert(iter != NULL);
+
+       while (!iter_done(iter))
+       {
+               EC_FALSE(iter_next(iter));
+               EC_FALSE(ram_write(hif, iter->address, iter->data, iter->data_len));
+
+               if (verify)
+               {
+                       uint8_t chunk[MAX_CHUNK];
+
+                       EC_FALSE(ram_read(hif, iter->address, chunk, iter->data_len));
+                       EC_NZ(memcmp(iter->data, chunk, iter->data_len));
+               }
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool update_ram_verify(HIF hif, struct iter *iter)
+{
+       assert(hif != hifInvalid);
+       assert(iter != NULL);
+
+       while (!iter_done(iter))
+       {
+               uint8_t chunk[MAX_CHUNK];
+
+               EC_FALSE(iter_next(iter));
+               EC_FALSE(ram_read(hif, iter->address, chunk, iter->data_len));
+               EC_NZ(memcmp(iter->data, chunk, iter->data_len));
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool update_flash_erase(HIF hif)
+{
+       assert(hif != hifInvalid);
+
+       uint8_t cfi_buf[0x100];
+       uint8_t num_erase_regions;
+       uint32_t address = 0;
+
+       // Read and verify CFI query information
+       EC_FALSE(flash_cfi(hif, 0, cfi_buf, sizeof(cfi_buf)));
+       EC_FALSE(cfi_buf[0x10] == 'Q');
+       EC_FALSE(cfi_buf[0x11] == 'R');
+       EC_FALSE(cfi_buf[0x12] == 'Y');
+
+       // Flash chips can contain multiple "erase regions," each of which
+       // contains some number of equal-sized eraseable blocks
+
+       // Read the number of regions from the CFI query and loop for each
+       num_erase_regions = cfi_buf[0x2c];
+       for (size_t i = 0; i < num_erase_regions; i++)
+       {
+               size_t block_info_idx = 0x2d + (4 * i);
+               assert(block_info_idx < 0x100);  // Just in case
+
+               // Read the geometry of this region's blocks from the CFI query
+               uint32_t block_count = ((cfi_buf[block_info_idx + 1] << 8) |
+                                        cfi_buf[block_info_idx + 0]) + 1;
+               uint32_t block_size  = ((cfi_buf[block_info_idx + 3] << 8) |
+                                        cfi_buf[block_info_idx + 2]) * 256;
+               if (block_size == 0) block_size = 128;
+               printf("Erase region %zi - block size: %08x  block_count: %08x\n", i, block_size, block_count);
+
+               // Erase each block within this region
+               for (size_t j = 0; j < block_count; j++)
+               {
+                       printf("  block %zi at 0x%08x\n", j, address);
+                       EC_FALSE(flash_erase(hif, address));
+                       address += block_size;
+               }
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool update_flash_read(HIF hif, struct iter *iter, uint32_t addr, size_t data_len)
+{
+       assert(hif != hifInvalid);
+       assert(iter != NULL);
+
+       uint8_t chunk[MAX_CHUNK];
+       size_t bytes_read = 0;
+
+       while (bytes_read < data_len)
+       {
+               size_t num_cur_bytes = MIN(data_len - bytes_read, MAX_CHUNK);
+
+               EC_FALSE(flash_read(hif, addr, chunk, num_cur_bytes));
+               EC_FALSE(iter_append(iter, addr, chunk, num_cur_bytes));
+
+               addr += num_cur_bytes;
+               bytes_read += num_cur_bytes;
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool update_flash_write(HIF hif, struct iter *iter, bool erase, bool verify)
+{
+       assert(hif != hifInvalid);
+       assert(iter != NULL);
+
+       uint32_t unerased_watermark = 0;
+       uint8_t cfi_buf[0x100];
+       uint8_t num_erase_regions;
+
+       // Read and verify CFI query information
+       EC_FALSE(flash_cfi(hif, 0, cfi_buf, sizeof(cfi_buf)));
+       EC_FALSE(cfi_buf[0x10] == 'Q');
+       EC_FALSE(cfi_buf[0x11] == 'R');
+       EC_FALSE(cfi_buf[0x12] == 'Y');
+
+       // Read the number of regions from the CFI query and loop for each
+       num_erase_regions = cfi_buf[0x2c];
+
+       while (!iter_done(iter))
+       {
+               EC_FALSE(iter_next(iter));
+               if (erase)
+               {
+                       // TODO
+               }
+               EC_FALSE(flash_write(hif, iter->address, iter->data, iter->data_len));
+
+               if (verify)
+               {
+                       uint8_t chunk[MAX_CHUNK];
+
+                       EC_FALSE(flash_read(hif, iter->address, chunk, iter->data_len));
+                       EC_NZ(memcmp(iter->data, chunk, iter->data_len));
+               }
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool update_flash_verify(HIF hif, struct iter *iter)
+{
+       assert(hif != hifInvalid);
+       assert(iter != NULL);
+
+       while (!iter_done(iter))
+       {
+               uint8_t chunk[MAX_CHUNK];
+
+               EC_FALSE(flash_read(hif, iter->address, chunk, iter->data_len));
+               EC_NZ(memcmp(iter->data, chunk, iter->data_len));
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+// -----------------------------------------------------------------------------
+
+
+void usage(void)
+{
+       fprintf(stderr,
+               "Driver program for Digilent FPGA/CPLD board memory\n"
+               "\n"
+               "Usage: digdude [options]\n"
+               "\nConnection options:\n"
+               "  -p, --part             Specify device (default: \"Nexys2\")\n"
+               "  -P, --port             Epp port number (default: 0)\n"
+               "\nOperation options:\n"
+               "  -U, --memory <memstr>:r|w|v:<filename>[:format]\n"
+               "                         Carry out memory operation,\n"
+               "                         multiple -U options can be specified\n"
+               "  -D, --noerase          Disable auto-erase for flash memory\n"
+               "  -e, --erase            Perform a full flash erase\n"
+               "  -E, --exitspec         Specify device state upon completion\n"
+               "  -n, --test-memory      Do not write to device during -U\n"
+               "  -V, --noverify-memory  Do not automatically verify during -U\n"
+               "\nOutput options:\n"
+               "  -v, --verbose          Verbose output; -v -v for more\n"
+               "  -q, --quell            Quell progress output; -q -q for less\n"
+               "  -?, --help             Display this message\n"
+               "\n"
+               );
+}
+
+
+struct update
+{
+       struct iter iter;
+       enum mem mem;
+       enum mode mode;
+       enum format format;
+};
+
+
+bool parse_update(char *optarg, struct update *update_out)
+{
+       assert(optarg != NULL);
+       assert(update_out != NULL);
+
+       char *_optarg;
+       char *token;
+       char *filename;
+
+       EC_NULL(token = strtok_r(optarg, ":", &_optarg));
+       if (!strcmp(token, "ram"))
+       {
+               update_out->mem = RAM;
+       }
+       else if (!strcmp(token, "flash"))
+       {
+               update_out->mem = FLASH;
+       }
+       else if (!strcmp(token, "cfi"))
+       {
+               update_out->mem = CFI;
+       }
+       else
+       {
+               EC_FAIL;
+       }
+
+       EC_NULL(token = strtok_r(NULL, ":", &_optarg));
+       if (strlen(token) > 1) EC_FAIL;
+       switch (token[0])
+       {
+       case 'r': update_out->mode = M_READ;   break;
+       case 'w': update_out->mode = M_WRITE;  break;
+       case 'v': update_out->mode = M_VERIFY; break;
+       default:  EC_FAIL;
+       }
+
+       EC_NULL(token = strtok_r(NULL, ":", &_optarg));
+       filename = token;
+
+       update_out->format = F_RAWBIN;
+       token = strtok_r(NULL, ":", &_optarg);
+       if (token != NULL)
+       {
+               switch (token[0])
+               {
+               case 'i': update_out->format = F_INTELHEX; break;
+               case 's': update_out->format = F_MOTOSREC; break;
+               case 'b': update_out->format = F_RAWBIN;   break;
+               default: EC_FAIL;
+               }
+       }
+
+       if (update_out->mode == M_READ)
+       {
+               // Read from memory, write to file
+               EC_FALSE(iter_write(&update_out->iter, filename, update_out->format));
+       }
+       else
+       {
+               // Write/verify memory, read from file
+               EC_FALSE(iter_read(&update_out->iter, filename, update_out->format));
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+#define MAX_UPDATES 8
+
+
+int main(int argc, char **argv)
+{
+       // Parsed options
+       bool erase_auto = true;
+       bool erase_explicit = false;
+       char *part = "Nexys2";
+       int port = 0;
+       int quell = 0;
+       int verbose = 0;
+       bool verify = true;
+       enum exitspec exitspec = X_RESET;
+       struct update updates[MAX_UPDATES];
+       size_t num_updates = 0;
+
+       // Board connection handle
+       HIF hif;
+
+       // Option parsing
+       int opt;
+       int option_idx = 0;
+       struct option longopts[] = {
+               {"help",            no_argument,       NULL, '?'},
+               {"noerase",         no_argument,       NULL, 'D'},
+               {"erase",           no_argument,       NULL, 'e'},
+               {"exitspec",        required_argument, NULL, 'E'},
+               {"test-memory",     no_argument,       NULL, 'n'},
+               {"part",            required_argument, NULL, 'p'},
+               {"port",            required_argument, NULL, 'P'},
+               {"quell",           no_argument,       NULL, 'q'},
+               {"memory",          required_argument, NULL, 'U'},
+               {"verbose",         no_argument,       NULL, 'v'},
+               {"noverify-memory", no_argument,       NULL, 'V'},
+       };
+
+       while ((opt = getopt_long(argc, argv, "?DeE:l:p:P:qU:vV", longopts, &option_idx)) != -1)
+       {
+               switch (opt)
+               {
+               case '?': usage(); exit(0); break;
+               case 'D': erase_auto = false; break;
+               case 'e': erase_explicit = true; erase_auto = false; break;
+
+               case 'E':
+                       if (!strcmp(optarg, "reset"))
+                       {
+                               exitspec = X_RESET;
+                       }
+                       else if (!strcmp(optarg, "noreset"))
+                       {
+                               exitspec = X_NORESET;
+                       }
+                       else
+                       {
+                               EC_FAIL;
+                       }
+                       break;
+
+               case 'p': part = optarg; break;
+               case 'P': port = strtol(optarg, NULL, 0); break;
+               case 'q': quell++; break;
+
+               case 'U':
+                       if (num_updates >= MAX_UPDATES) EC_FAIL;
+                       EC_FALSE(parse_update(optarg, &updates[num_updates]));
+                       num_updates++;
+                       break;
+
+               case 'v': verbose++; break;
+               case 'V': verify = false; break;
+
+               default:
+                       fprintf(stderr, "Invalid option: -%c\n\n", opt);
+                       usage();
+                       exit(1);
+                       break;
+               }
+       }
+
+
+       // Open connection to board
+       EC_FALSE(epp_open(&hif, part, port));
+
+       // Optional explicit full-chip erase
+       if (erase_explicit) EC_FALSE(update_flash_erase(hif));
+
+       // Possibly multiple updates
+       for (size_t i = 0; i < num_updates; i++)
+       {
+               switch (updates[i].mem)
+               {
+               case RAM:
+                       switch (updates[i].mode)
+                       {
+                       case M_READ: EC_FALSE(update_ram_read(hif, &updates[i].iter, 0x000000, 0x800000)); break;
+                       case M_WRITE: EC_FALSE(update_ram_write(hif, &updates[i].iter, verify)); break;
+                       case M_VERIFY: EC_FALSE(update_ram_verify(hif, &updates[i].iter)); break;
+                       default: assert(false); break;
+                       }
+                       break;
+
+               case FLASH:
+                       switch (updates[i].mode)
+                       {
+                       case M_READ: EC_FALSE(update_flash_read(hif, &updates[i].iter, 0x000000, 0x800000)); break;
+                       case M_WRITE: EC_FALSE(update_flash_write(hif, &updates[i].iter, erase_auto, verify)); break;
+                       case M_VERIFY: EC_FALSE(update_flash_verify(hif, &updates[i].iter)); break;
+                       default: assert(false); break;
+                       }
+                       break;
+
+               case CFI:
+                       switch (updates[i].mode)
+                       {
+                       case M_READ:
+                               //EC_FALSE(flash_cfi(hif, 0, buffer, sizeof(buffer)));
+                               //EC_FALSE(write_file(updates[i].filename, updates[i].format, buffer, sizeof(buffer)));
+                               break;
+
+                       case M_WRITE: EC_FAIL; break;
+                       case M_VERIFY: EC_FAIL; break;
+                       default: assert(false); break;
+                       }
+                       break;
+
+               default: assert(false); break;
+               }
+       }
+
+       // Return to desired exit condition
+       switch (exitspec)
+       {
+       case X_RESET:   EC_FALSE(set_exit(hif, true)); break;
+       case X_NORESET: EC_FALSE(set_exit(hif, false)); break;
+       default: assert(false); break;
+       }
+
+       // Close connection to board and done
+       epp_close(hif);
+       return 0;
+
+EC_CLEANUP_BEGIN
+       epp_close(hif);
+       ec_print();
+       return 1;
+EC_CLEANUP_END
+}
+
diff --git a/projects/nexys2_host_controller/host/ec.c b/projects/nexys2_host_controller/host/ec.c
new file mode 100644 (file)
index 0000000..d61d522
--- /dev/null
@@ -0,0 +1,71 @@
+#include "ec.h"
+
+#define EC_MAX 32
+
+
+// -----------------------------------------------------------------------------
+
+
+struct ec_node
+{
+       int ec_errno;
+       EC_ERRTYPE type;
+       const char *func;
+       const char *file;
+       int line;
+       const char *str_line;
+};
+
+
+static struct ec_node ec_stack[EC_MAX];
+static size_t ec_top = 0;
+
+
+// -----------------------------------------------------------------------------
+
+
+const bool ec_in_cleanup = false;
+
+
+void ec_push(const char *func, const char *file, int line, const char *str_line,
+             int errno_arg, EC_ERRTYPE type)
+{
+       assert(ec_top < EC_MAX);
+
+       ec_stack[ec_top].ec_errno = errno_arg;
+       ec_stack[ec_top].type = type;
+       ec_stack[ec_top].func = func;
+       ec_stack[ec_top].file = file;
+       ec_stack[ec_top].line = line;
+       ec_stack[ec_top].str_line = str_line;
+
+       ec_top++;
+}
+
+
+void ec_print(void)
+{
+       fprintf(stderr, "ERROR STACK:\n");
+       for (int i = ec_top - 1; i >= 0; i--)
+       {
+               fprintf(stderr, "%3i: %s:%i (%s) %s\n",
+                       i,
+                       ec_stack[i].file,
+                       ec_stack[i].line,
+                       ec_stack[i].func,
+                       ec_stack[i].str_line);
+       }
+}
+
+
+void ec_reinit(void)
+{
+       ec_top = 0;
+}
+
+
+void ec_warn(void)
+{
+       fprintf(stderr, "WARNING: Control flowed into EC_CLEANUP_BEGIN\n");
+}
+
diff --git a/projects/nexys2_host_controller/host/ec.h b/projects/nexys2_host_controller/host/ec.h
new file mode 100644 (file)
index 0000000..f99556f
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+
+Shamelessly stolen from Advanced UNIX Programming 2nd Ed. by Marc J. Rochkind
+
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+
+
+extern const bool ec_in_cleanup;
+
+
+typedef enum {
+       EC_ERRNO,
+       EC_EAI,
+       EC_GETDATE,
+       EC_NONE,
+} EC_ERRTYPE;
+
+
+#define EC_CLEANUP_BEGIN \
+       ec_warn(); \
+       ec_cleanup_begin: \
+       { \
+               bool ec_in_cleanup = true; \
+               (void)ec_in_cleanup;  // Suppress "unused" warning
+
+
+#define EC_CLEANUP_END \
+       }
+
+
+// For functions that set ERRNO and return a specific error indicator value
+#define EC_CMP(var, errrtn) \
+       { \
+               assert(!ec_in_cleanup); \
+               if ((intptr_t)(var) == (intptr_t)(errrtn)) { \
+                       ec_push(__func__, __FILE__, __LINE__, #var, errno, EC_ERRNO); \
+                       goto ec_cleanup_begin; \
+               } \
+       }
+
+
+// For functions that return an ERRNO value
+#define EC_RV(var) \
+       { \
+               int errrtn; \
+               assert(!ec_in_cleanup); \
+               if ((errrtn = (var)) != 0) { \
+                       ec_push(__func__, __FILE__, __LINE__, #var, errrtn, EC_ERRNO); \
+                       goto ec_cleanup_begin; \
+               } \
+       }
+
+
+#define EC_AI(var) \
+       { \
+               int errrtn; \
+               assert(!ec_in_cleanup); \
+               if ((errrtn = (var)) != 0) { \
+                       ec_push(__func__, __FILE__, __LINE__, #var, errrtn, EC_EAI); \
+                       goto ec_cleanup_begin; \
+               } \
+       }
+
+
+#define EC_NEG1(var)        EC_CMP(var, -1)
+#define EC_NULL(var)        EC_CMP(var, NULL)
+#define EC_FALSE(var)       EC_CMP(var, false)
+#define EC_EOF(var)         EC_CMP(var, EOF)
+#define EC_NZ(var)          { if ((var) != 0) EC_FAIL }
+#define EC_NE(var, errrtn)  { if ((var) != (errrtn)) EC_FAIL }
+
+#define EC_FAIL       EC_CMP(0, 0)
+#define EC_CLEANUP    goto ec_cleanup_begin;
+
+#define EC_FLUSH(str) \
+       { \
+               ec_print(); \
+               ec_reinit(); \
+       }
+
+
+void ec_push(const char *func, const char *file, int line, const char *str_line,
+             int errno_arg, EC_ERRTYPE type);
+void ec_print(void);
+void ec_reinit(void);
+void ec_warn(void);
+
diff --git a/projects/nexys2_host_controller/host/epp_read.c b/projects/nexys2_host_controller/host/epp_read.c
new file mode 100644 (file)
index 0000000..2cff490
--- /dev/null
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+// Digilent SDK
+#include "dpcdecl.h"
+#include "depp.h"
+#include "dmgr.h"
+
+#include "ec.h"
+
+
+// -----------------------------------------------------------------------------
+
+
+int main(int argc, char **argv)
+{
+       long address;
+       uint8_t value;
+       HIF hif = hifInvalid;
+       char *device = "Nexys2";
+       int32_t port_count;
+       uint32_t port_properties;
+       int32_t port = 0;
+       char version[cchVersionMax];
+
+       // -----------------------------------------------------------
+       // Parse commnd line args
+
+       if (argc != 2)
+       {
+               fprintf(stderr, "Wrong number of arguments\n");
+               return 1;
+       }
+
+       address = strtol(argv[1], NULL, 0);  // TODO: error handling
+
+       // -----------------------------------------------------------
+       // Initilize EPP interface
+
+       // Print DEPP version
+       EC_FALSE(DeppGetVersion(version));
+       //printf("DEPP version: %s\n", version);
+
+       // Open device
+       EC_FALSE(DmgrOpen(&hif, device));
+       //printf("Opened device \"%s\"\n", device);
+
+       // Check number of DEPP ports
+       EC_FALSE(DeppGetPortCount(hif, &port_count));
+       //printf("Port count: %" PRIi32 "\n", port_count);
+       for (int32_t i = 0; i < port_count; i++)
+       {
+               EC_FALSE(DeppGetPortProperties(hif, i, &port_properties));
+               //printf("Port %" PRIi32 " properties: 0x%08" PRIx32 "\n", i, port_properties);
+       }
+       if (port_count != 1)
+       {
+               fprintf(stderr, "TODO: number of DEPP ports: %" PRIi32 "\n", port_count);
+               EC_FAIL;
+       }
+
+       // Enable DEPP port
+       EC_FALSE(DeppEnableEx(hif, port))
+       //printf("Opened EPP port %" PRIi32 "\n", port);
+
+       // Set timeout
+       // uint32_t timeout;
+       // EC_FALSE(DeppSetTimeout(hif, 0xffffffff, &timeout));
+       // printf("Timeout set to %" PRIu32 "ns\n", timeout);
+
+       // -----------------------------------------------------------
+       // Read EPP register and report
+
+       EC_FALSE(DeppGetReg(hif, address, &value, false));
+       printf("Read:  0x%02lx == 0x%02x\n", address, value);
+
+       // -----------------------------------------------------------
+       // Close EPP session
+
+       if (hif != hifInvalid)
+       {
+               DeppDisable(hif);
+               DmgrClose(hif);
+               //printf("Closed session\n");
+       }
+
+       return 0;
+
+EC_CLEANUP_BEGIN
+       if (hif != hifInvalid)
+       {
+               DeppDisable(hif);
+               DmgrClose(hif);
+       }
+
+       ec_print();
+       return 1;
+EC_CLEANUP_END
+}
+
diff --git a/projects/nexys2_host_controller/host/epp_write.c b/projects/nexys2_host_controller/host/epp_write.c
new file mode 100644 (file)
index 0000000..707af81
--- /dev/null
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+// Digilent SDK
+#include "dpcdecl.h"
+#include "depp.h"
+#include "dmgr.h"
+
+#include "ec.h"
+
+
+// -----------------------------------------------------------------------------
+
+
+int main(int argc, char **argv)
+{
+       long address;
+       long value;
+       HIF hif = hifInvalid;
+       char *device = "Nexys2";
+       int32_t port_count;
+       uint32_t port_properties;
+       int32_t port = 0;
+       char version[cchVersionMax];
+
+       // -----------------------------------------------------------
+       // Parse commnd line args
+
+       if (argc != 3)
+       {
+               fprintf(stderr, "Wrong number of arguments\n");
+               return 1;
+       }
+
+       address = strtol(argv[1], NULL, 0);  // TODO: error handling
+       value = strtol(argv[2], NULL, 0);    // TODO: error handling
+
+       // -----------------------------------------------------------
+       // Initilize EPP interface
+
+       // Print DEPP version
+       EC_FALSE(DeppGetVersion(version));
+       //printf("DEPP version: %s\n", version);
+
+       // Open device
+       EC_FALSE(DmgrOpen(&hif, device));
+       //printf("Opened device \"%s\"\n", device);
+
+       // Check number of DEPP ports
+       EC_FALSE(DeppGetPortCount(hif, &port_count));
+       //printf("Port count: %" PRIi32 "\n", port_count);
+       for (int32_t i = 0; i < port_count; i++)
+       {
+               EC_FALSE(DeppGetPortProperties(hif, i, &port_properties));
+               //printf("Port %" PRIi32 " properties: 0x%08" PRIx32 "\n", i, port_properties);
+       }
+       if (port_count != 1)
+       {
+               fprintf(stderr, "TODO: number of DEPP ports: %" PRIi32 "\n", port_count);
+               EC_FAIL;
+       }
+
+       // Enable DEPP port
+       EC_FALSE(DeppEnableEx(hif, port));
+       //printf("Opened EPP port %" PRIi32 "\n", port);
+
+       // Set timeout
+       // uint32_t timeout;
+       // EC_FALSE(DeppSetTimeout(hif, 0xffffffff, &timeout));
+       // printf("Timeout set to %" PRIu32 "ns\n", timeout);
+
+       // -----------------------------------------------------------
+       // Write EPP register and report
+
+       EC_FALSE(DeppPutReg(hif, address, (uint8_t)value, false));
+       printf("Write: 0x%02x <= 0x%02x\n", (unsigned)address, (unsigned)value);
+
+       // -----------------------------------------------------------
+       // Close EPP session
+
+       if (hif != hifInvalid)
+       {
+               DeppDisable(hif);
+               DmgrClose(hif);
+               //printf("Closed session\n");
+       }
+
+       return 0;
+
+EC_CLEANUP_BEGIN
+       if (hif != hifInvalid)
+       {
+               DeppDisable(hif);
+               DmgrClose(hif);
+       }
+
+       ec_print();
+       return 1;
+EC_CLEANUP_END
+}
+
diff --git a/projects/nexys2_host_controller/host/fileformat.c b/projects/nexys2_host_controller/host/fileformat.c
new file mode 100644 (file)
index 0000000..e7f3d89
--- /dev/null
@@ -0,0 +1,270 @@
+#include <stdio.h>
+
+#include "ec.h"
+
+#include "fileformat.h"
+
+
+// -----------------------------------------------------------------------------
+
+
+bool iter_next_ihex(struct iter *iter)
+{
+       assert(iter != NULL);
+       assert(iter->source != NULL);
+       assert(iter->format == F_INTELHEX);
+
+       (void)iter;
+
+       return false;
+}
+
+
+bool iter_next_srec(struct iter *iter)
+{
+       assert(iter != NULL);
+       assert(iter->source != NULL);
+       assert(iter->format == F_MOTOSREC);
+
+       (void)iter;
+
+       return false;
+}
+
+
+bool iter_next_bin(struct iter *iter)
+{
+       assert(iter != NULL);
+       assert(iter->source != NULL);
+       assert(iter->format == F_RAWBIN);
+
+       iter->address += iter->data_len;
+       iter->data_len = 0;
+
+       iter->data_len = fread(&iter->data[0], 1, MAX_CHUNK, iter->source);
+       EC_NZ(ferror(iter->source));
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool iter_append_ihex(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len)
+{
+       assert(iter != NULL);
+       assert(iter->source != NULL);
+       assert(data != NULL);
+
+       (void)iter;
+       (void)addr;
+       (void)data;
+       (void)data_len;
+
+       return false;
+}
+
+
+bool iter_append_srec(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len)
+{
+       assert(iter != NULL);
+       assert(iter->source != NULL);
+       assert(data != NULL);
+
+       (void)iter;
+       (void)addr;
+       (void)data;
+       (void)data_len;
+
+       return false;
+}
+
+
+bool iter_append_bin(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len)
+{
+       assert(iter != NULL);
+       assert(iter->source != NULL);
+       assert(data != NULL);
+
+       // Successive calls must be contiguous for this format
+       EC_FALSE(iter->address == addr);
+       EC_NE(fwrite(data, 1, data_len, iter->source), data_len);
+       EC_NZ(ferror(iter->source));
+       iter->address += data_len;
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool write_ihex(FILE *f, uint8_t *data, size_t data_len)
+{
+       assert(f != NULL);
+       assert(data != NULL);
+
+       (void)f;
+       (void)data;
+       (void)data_len;
+
+       return false;
+}
+
+
+bool write_srec(FILE *f, uint8_t *data, size_t data_len)
+{
+       assert(f != NULL);
+       assert(data != NULL);
+
+       (void)f;
+       (void)data;
+       (void)data_len;
+
+       return false;
+}
+
+
+bool write_bin(FILE *f, uint8_t *data, size_t data_len)
+{
+       assert(f != NULL);
+       assert(data != NULL);
+
+       EC_NE(fwrite(data, 1, data_len, f), data_len);
+       EC_NZ(ferror(f));
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+// -----------------------------------------------------------------------------
+
+
+bool iter_read(struct iter *iter, const char *filename, enum format format)
+{
+       assert(iter != NULL);
+       assert(filename != NULL);
+       assert(format == F_INTELHEX ||
+              format == F_MOTOSREC ||
+              format == F_RAWBIN);
+
+       iter->source = NULL;
+       iter->format = format;
+       iter->address = 0;
+       iter->data_len = 0;
+       EC_NULL(iter->source = fopen(filename, "r"));
+       return true;
+
+EC_CLEANUP_BEGIN
+       fclose(iter->source);
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool iter_write(struct iter *iter, const char *filename, enum format format)
+{
+       assert(iter != NULL);
+       assert(filename != NULL);
+       assert(format == F_INTELHEX ||
+              format == F_MOTOSREC ||
+              format == F_RAWBIN);
+
+       iter->source = NULL;
+       iter->format = format;
+       iter->address = 0;
+       iter->data_len = 0;
+       EC_NULL(iter->source = fopen(filename, "w"));
+       return true;
+
+EC_CLEANUP_BEGIN
+       fclose(iter->source);
+       return false;
+EC_CLEANUP_END
+}
+
+
+void iter_close(struct iter *iter)
+{
+       fclose(iter->source);
+       iter->source = NULL;
+}
+
+
+bool iter_next(struct iter *iter)
+{
+       switch (iter->format)
+       {
+       case F_INTELHEX: return iter_next_ihex(iter);
+       case F_MOTOSREC: return iter_next_srec(iter);
+       case F_RAWBIN:   return iter_next_bin(iter);
+       default: assert(false); return false;
+       }
+       assert(false);
+       return false;
+}
+
+
+bool iter_done(struct iter *iter)
+{
+       assert(iter != NULL);
+       assert(iter->source != NULL);
+
+       return (bool)feof(iter->source);
+}
+
+
+bool iter_append(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len)
+{
+       assert(iter != NULL);
+       assert(iter->source != NULL);
+       assert(data != NULL);
+
+       switch (iter->format)
+       {
+       case F_INTELHEX: EC_FALSE(iter_append_ihex(iter, addr, data, data_len)); break;
+       case F_MOTOSREC: EC_FALSE(iter_append_srec(iter, addr, data, data_len)); break;
+       case F_RAWBIN:   EC_FALSE(iter_append_bin(iter, addr, data, data_len)); break;
+       default: assert(false); EC_FAIL; break;
+       }
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool write_file(const char *filename, enum format format, uint8_t *data, size_t data_len)
+{
+       assert(filename != NULL);
+       assert(format == F_INTELHEX ||
+              format == F_MOTOSREC ||
+              format == F_RAWBIN);
+       assert(data != NULL);
+
+       FILE *dest = NULL;
+
+       EC_NULL(dest = fopen(filename, "w"));
+
+       switch (format)
+       {
+       case F_INTELHEX: EC_FALSE(write_ihex(dest, data, data_len)); break;
+       case F_MOTOSREC: EC_FALSE(write_srec(dest, data, data_len)); break;
+       case F_RAWBIN:   EC_FALSE(write_bin(dest, data, data_len)); break;
+       default: assert(false); EC_FAIL; break;
+       }
+
+       EC_EOF(fclose(dest));
+       return true;
+
+EC_CLEANUP_BEGIN
+       fclose(dest);
+       return false;
+EC_CLEANUP_END
+}
+
diff --git a/projects/nexys2_host_controller/host/fileformat.h b/projects/nexys2_host_controller/host/fileformat.h
new file mode 100644 (file)
index 0000000..d3c5c47
--- /dev/null
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <stdio.h>
+
+
+enum format { F_INTELHEX, F_MOTOSREC, F_RAWBIN };
+
+
+#define MAX_CHUNK ((size_t)4096)
+
+struct iter
+{
+       FILE *source;
+       enum format format;
+
+       uint32_t address;
+       size_t data_len;
+       uint8_t data[MAX_CHUNK];
+};
+
+
+bool iter_read(struct iter *iter, const char *filename, enum format format);
+bool iter_write(struct iter *iter, const char *filename, enum format format);
+void iter_close(struct iter *iter);
+
+// Read streams
+bool iter_next(struct iter *iter);
+bool iter_done(struct iter *iter);
+
+// Write streams
+bool iter_append(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len);
+
diff --git a/projects/nexys2_host_controller/host/memctrl.c b/projects/nexys2_host_controller/host/memctrl.c
new file mode 100644 (file)
index 0000000..031701f
--- /dev/null
@@ -0,0 +1,361 @@
+#include "ec.h"
+
+#include "memctrl.h"
+
+// Digilent SDK
+#include <dpcdecl.h>
+#include <depp.h>
+#include <dmgr.h>
+
+// -----------------------------------------------------------------------------
+
+
+// Board registers
+#define REG_CTRL    0x00
+#define REG_FLAGS   0x01
+#define REG_MBOX    0x02
+#define REG_LEDSW   0x03
+#define REG_SSEG0   0x04
+#define REG_SSEG1   0x05
+#define REG_SSEG2   0x06
+#define REG_SSEG3   0x07
+
+// REG_CTRL register bits
+#define REG_CTRL_DEV_RST  0x80  // Put the device into reset
+#define REG_CTRL_OOB_MEM  0x40  // Give host control over memory
+
+
+// Out-of-band memory controller registers
+#define MEM_CTRL    0x08
+#define MEM_ADDR_L  0x09
+#define MEM_ADDR_M  0x0a
+#define MEM_ADDR_H  0x0b
+#define MEM_DATA_L  0x0c
+#define MEM_DATA_H  0x0d
+
+// MEM_CTRL register bits
+#define MEM_CTRL_FLS_RST  0x80
+#define MEM_CTRL_FLS_CS   0x40
+#define MEM_CTRL_RAM_CS   0x04
+#define MEM_CTRL_RAM_CRE  0x02
+#define MEM_CTRL_AUT_CNT  0x01  // Auto-increment address
+
+
+// Flash commands
+#define CMD_PECR    0x40  // Program Enhanced Configuration Register
+#define CMD_PECR_C  0x04  // Program Enhanced Configuration Register (Confirm)
+#define CMD_POTP    0xc0  // Program OTP Register
+#define CMD_STSCLR  0x50  // Clear Status Register
+#define CMD_PSCR    0xb8  // Program STS Configuration Register
+#define CMD_RDAR    0xff  // Read Array
+#define CMD_RDSR    0x70  // Read Status Register
+#define CMD_RDID    0x90  // Read Identifier Codes
+#define CMD_CFIQ    0x98  // CFI Query
+#define CMD_PWRD    0x40  // Word/Byte Program
+#define CMD_PBUF    0xe8  // Buffered Program
+#define CMD_PBUF_C  0xd0  // Buffered Program (Confirm)
+#define CMD_EBLK    0x20  // Block Erase
+#define CMD_EBLK_C  0xd0  // Block Erase (Confirm)
+#define CMD_SUSP    0xb0  // Program/Erase Suspend
+#define CMD_RESM    0xd0  // Program/Erase Resume
+#define CMD_BLK     0x60  // Lock/Unlock Block
+#define CMD_BLKL    0x01  // Lock Block (Confirm)
+#define CMD_BLKU    0xd0  // Unlock Block (Confirm)
+
+// Flash status register bits
+#define STS_RDY     0x80  // Ready Status
+#define STS_SUSP_E  0x40  // Erase Suspend Status
+#define STS_ERR_E   0x20  // Erase Error    \__ Command Sequence Error
+#define STS_ERR_P   0x10  // Program Error  /
+#define STS_ERR_V   0x08  // Program/Erase Voltage Error
+#define STS_SUSP_P  0x04  // Program Suspend Status
+#define STS_ERR_L   0x02  // Bit-Locked Error
+
+
+// -----------------------------------------------------------------------------
+
+
+bool epp_open(HIF *hif_out, char *device, int32_t port)
+{
+       EC_FALSE(DmgrOpen(hif_out, device));
+       EC_FALSE(DeppEnableEx(*hif_out, port));
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+void epp_close(HIF hif)
+{
+       if (hif != hifInvalid)
+       {
+               DeppDisable(hif);
+               DmgrClose(hif);
+       }
+}
+
+
+bool ram_read(HIF hif, uint32_t addr, uint8_t *data_out, size_t data_len)
+{
+       assert(hif != hifInvalid);
+       assert((addr & 0xff000000) == 0x00000000);  // Addresses are 24 bits
+       assert((data_len & 1) == 0);                // Words are two bytes
+
+       // Reset device and select oobmem
+       EC_FALSE(DeppPutReg(hif, REG_CTRL, REG_CTRL_DEV_RST |
+                                          REG_CTRL_OOB_MEM, false));
+
+       // Select ram
+       EC_FALSE(DeppPutReg(hif, MEM_CTRL, MEM_CTRL_FLS_RST |
+                                          MEM_CTRL_FLS_CS  |
+                                          MEM_CTRL_AUT_CNT, false));
+
+       // Write address
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_L, (addr >>  0) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_M, (addr >>  8) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_H, (addr >> 16) & 0xff, false));
+
+       for (size_t i = 0; i < data_len; i += 2)
+       {
+               // Read data (little endian)
+               EC_FALSE(DeppGetReg(hif, MEM_DATA_L, &data_out[i + 0], false));
+               EC_FALSE(DeppGetReg(hif, MEM_DATA_H, &data_out[i + 1], false));
+
+               addr += 2;
+       }
+
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool ram_write(HIF hif, uint32_t addr, const uint8_t *data, size_t data_len)
+{
+       assert(hif != hifInvalid);
+       assert((addr & 0xff000000) == 0x00000000);  // Addresses are 24 bits
+       assert((data_len & 1) == 0);                // Words are two bytes
+
+       // Reset device and select oobmem
+       EC_FALSE(DeppPutReg(hif, REG_CTRL, REG_CTRL_DEV_RST |
+                                          REG_CTRL_OOB_MEM, false));
+
+       // Select ram
+       EC_FALSE(DeppPutReg(hif, MEM_CTRL, MEM_CTRL_FLS_RST |
+                                          MEM_CTRL_FLS_CS  |
+                                          MEM_CTRL_AUT_CNT, false));
+
+       // Write address
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_L, (addr >>  0) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_M, (addr >>  8) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_H, (addr >> 16) & 0xff, false));
+
+       for (size_t i = 0; i < data_len; i += 2)
+       {
+               // Write data (little endian)
+               EC_FALSE(DeppPutReg(hif, MEM_DATA_H, data[i + 1], false));
+               EC_FALSE(DeppPutReg(hif, MEM_DATA_L, data[i + 0], false));
+
+               addr += 2;
+       }
+
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool flash_read(HIF hif, uint32_t addr, uint8_t *data_out, size_t data_len)
+{
+       assert(hif != hifInvalid);
+       assert((addr & 0xff000000) == 0x00000000);  // Addresses are 24 bits
+       assert((data_len & 1) == 0);                // Words are two bytes
+
+       // Reset device and select oobmem
+       EC_FALSE(DeppPutReg(hif, REG_CTRL, REG_CTRL_DEV_RST |
+                                          REG_CTRL_OOB_MEM, false));
+
+       // Select flash
+       EC_FALSE(DeppPutReg(hif, MEM_CTRL, MEM_CTRL_FLS_RST |
+                                          MEM_CTRL_RAM_CS  |
+                                          MEM_CTRL_AUT_CNT, false));
+
+       // Read array mode
+       EC_FALSE(DeppPutReg(hif, MEM_DATA_H, 0x00, false));
+       EC_FALSE(DeppPutReg(hif, MEM_DATA_L, CMD_RDAR, false));
+
+       // Write address
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_L, (addr >>  0) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_M, (addr >>  8) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_H, (addr >> 16) & 0xff, false));
+
+       for (size_t i = 0; i < data_len; i += 2)
+       {
+               // Read data (little endian)
+               EC_FALSE(DeppGetReg(hif, MEM_DATA_L, &data_out[i + 0], false));
+               EC_FALSE(DeppGetReg(hif, MEM_DATA_H, &data_out[i + 1], false));
+
+               addr += 2;
+       }
+
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool flash_cfi(HIF hif, uint32_t addr, uint8_t *data, size_t data_len)
+{
+       assert(hif != hifInvalid);
+       assert(data != NULL);
+       assert((addr & 0xff000000) == 0x00000000);  // Addresses are 24 bits
+       //assert((data_len & 1) == 0);                // Words are two bytes
+
+       // Reset device and select oobmem
+       EC_FALSE(DeppPutReg(hif, REG_CTRL, REG_CTRL_DEV_RST |
+                                          REG_CTRL_OOB_MEM, false));
+
+       // Select flash
+       EC_FALSE(DeppPutReg(hif, MEM_CTRL, MEM_CTRL_FLS_RST |
+                                          MEM_CTRL_RAM_CS  |
+                                          MEM_CTRL_AUT_CNT, false));
+
+       // CFI Query command
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_L, 0x55, false));  // TODO: #define this
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_M, 0x00, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_H, 0x00, false));
+       EC_FALSE(DeppPutReg(hif, MEM_DATA_H, 0x00, false));
+       EC_FALSE(DeppPutReg(hif, MEM_DATA_L, CMD_CFIQ, false));
+
+       // Set initial address
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_L, (addr >>  0) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_M, (addr >>  8) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_H, (addr >> 16) & 0xff, false));
+
+       // Read data
+       for (size_t i = 0; i < data_len; i += 1)
+       {
+               EC_FALSE(DeppGetReg(hif, MEM_DATA_L, &data[i + 0], false));
+               //EC_FALSE(DeppGetReg(hif, MEM_DATA_H, &data[i + 0], false));
+       }
+
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool flash_erase(HIF hif, uint32_t addr)
+{
+       assert(hif != hifInvalid);
+       assert((addr & 0xff000000) == 0x00000000);  // Addresses are 24 bits
+
+       uint8_t status;
+
+       // Reset device and select oobmem
+       EC_FALSE(DeppPutReg(hif, REG_CTRL, REG_CTRL_DEV_RST |
+                                          REG_CTRL_OOB_MEM, false));
+
+       // Select flash
+       EC_FALSE(DeppPutReg(hif, MEM_CTRL, MEM_CTRL_FLS_RST |
+                                          MEM_CTRL_RAM_CS, false));
+
+       // Write address
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_L, (addr >>  0) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_M, (addr >>  8) & 0xff, false));
+       EC_FALSE(DeppPutReg(hif, MEM_ADDR_H, (addr >> 16) & 0xff, false));
+
+       // Page erase command
+       EC_FALSE(DeppPutReg(hif, MEM_DATA_H, 0x00, false));
+       EC_FALSE(DeppPutReg(hif, MEM_DATA_L, 0x20, false));
+       EC_FALSE(DeppPutReg(hif, MEM_DATA_L, 0xd0, false));
+
+       // Wait for completion
+       do
+       {
+               EC_FALSE(DeppGetReg(hif, MEM_DATA_L, &status, false));
+       } while ((status & 0x80) == 0x00);
+
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool flash_write(HIF hif, uint32_t addr, const uint8_t *data, size_t data_len)
+{
+       assert(hif != hifInvalid);
+       assert((addr & 0xff000000) == 0x00000000);  // Addresses are 24 bits
+       assert((data_len & 1) == 0);                // Words are two bytes
+
+       uint8_t status;
+
+       // Reset device and select oobmem
+       EC_FALSE(DeppPutReg(hif, REG_CTRL, REG_CTRL_DEV_RST |
+                                          REG_CTRL_OOB_MEM, false));
+
+       // Select flash
+       EC_FALSE(DeppPutReg(hif, MEM_CTRL, MEM_CTRL_FLS_RST |
+                                          MEM_CTRL_RAM_CS, false));
+
+       for (size_t i = 0; i < data_len; i += 2)
+       {
+               // Write address (can't use auto-count here)
+               EC_FALSE(DeppPutReg(hif, MEM_ADDR_L, (addr >>  0) & 0xff, false));
+               EC_FALSE(DeppPutReg(hif, MEM_ADDR_M, (addr >>  8) & 0xff, false));
+               EC_FALSE(DeppPutReg(hif, MEM_ADDR_H, (addr >> 16) & 0xff, false));
+
+               // Write word command
+               EC_FALSE(DeppPutReg(hif, MEM_DATA_H, 0x00, false));
+               EC_FALSE(DeppPutReg(hif, MEM_DATA_L, 0x40, false));
+
+               // Word value (little endian)
+               EC_FALSE(DeppPutReg(hif, MEM_DATA_H, data[i + 1], false));
+               EC_FALSE(DeppPutReg(hif, MEM_DATA_L, data[i + 0], false));
+
+               // Wait for completion
+               do
+               {
+                       EC_FALSE(DeppGetReg(hif, MEM_DATA_L, &status, false));
+               } while ((status & 0x80) == 0x00);
+
+               addr += 2;
+       }
+
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
+
+bool set_exit(HIF hif, bool reset_device)
+{
+       assert(hif != hifInvalid);
+
+       uint8_t reg_ctrl = 0;
+
+       EC_FALSE(DeppGetReg(hif, REG_CTRL, &reg_ctrl, false));
+       reg_ctrl &= ~(REG_CTRL_OOB_MEM | REG_CTRL_DEV_RST);
+       if (reset_device) reg_ctrl |= REG_CTRL_DEV_RST;
+       EC_FALSE(DeppPutReg(hif, REG_CTRL, reg_ctrl, false));
+       return true;
+
+EC_CLEANUP_BEGIN
+       return false;
+EC_CLEANUP_END
+}
+
diff --git a/projects/nexys2_host_controller/host/memctrl.h b/projects/nexys2_host_controller/host/memctrl.h
new file mode 100644 (file)
index 0000000..2b1f1ce
--- /dev/null
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+// Defined here to prevent multiple definitions of constants from dpcdecl.h
+typedef uint32_t HIF;
+#define hifInvalid 0
+
+
+bool epp_open(HIF *hif_out, char *device, int32_t port);
+void epp_close(HIF hif);
+
+bool ram_read(HIF hif, uint32_t addr, uint8_t *data_out, size_t data_len);
+bool ram_write(HIF hif, uint32_t addr, const uint8_t *data, size_t data_len);
+
+bool flash_read(HIF hif, uint32_t addr, uint8_t *data_out, size_t data_len);
+bool flash_cfi(HIF hif, uint32_t addr, uint8_t *data_out, size_t data_len);
+bool flash_erase(HIF hif, uint32_t addr);
+bool flash_write(HIF hif, uint32_t addr, const uint8_t *data, size_t data_len);
+
+bool set_exit(HIF hif, bool reset_device);
+
diff --git a/projects/nexys2_host_controller/host/test.sh b/projects/nexys2_host_controller/host/test.sh
new file mode 100755 (executable)
index 0000000..dea46e0
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+set -e
+
+echo "Control reg:"
+./epp_read 0
+./epp_write 0 0xc0
+./epp_read 0
+echo "Mem ctrl:"
+./epp_read 8
+./epp_write 8 0xc0
+echo "Address 0"
+./epp_write 0x09 0x00
+./epp_write 0x0a 0x00
+./epp_write 0x0b 0x00
+echo "Write data 0xa5c3"
+./epp_write 0x0d 0xa5
+./epp_write 0x0c 0xc3
+echo "Address 2"
+./epp_write 0x09 0x02
+./epp_write 0x0a 0x00
+./epp_write 0x0b 0x00
+echo "Write data 0x5a3c"
+./epp_write 0x0d 0x5a
+./epp_write 0x0c 0x3c
+echo "Address 0"
+./epp_write 0x09 0x00
+./epp_write 0x0a 0x00
+./epp_write 0x0b 0x00
+echo "Read data"
+./epp_read 0x0c
+./epp_read 0x0d
+echo "Address 2"
+./epp_write 0x09 0x02
+./epp_write 0x0a 0x00
+./epp_write 0x0b 0x00
+echo "Read data"
+./epp_read 0x0c
+./epp_read 0x0d
+
diff --git a/projects/nexys2_host_controller/nexys2.vhd b/projects/nexys2_host_controller/nexys2.vhd
new file mode 100644 (file)
index 0000000..0b1d77f
--- /dev/null
@@ -0,0 +1,126 @@
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library nexys2_lib;
+
+
+entity nexys2 is
+       port (
+               clk_50:     in    std_logic;
+
+               -- EPP interface
+               EppASTB:    in    std_logic;
+               EppDSTB:    in    std_logic;
+               EppWRITE:   in    std_logic;
+               EppWAIT:    out   std_logic;
+               DB:         inout std_logic_vector(7 downto 0);
+
+               -- Memory interface
+               MemOE:      out   std_logic;
+               MemWR:      out   std_logic;
+
+               RamAdv:     out   std_logic;
+               RamCS:      out   std_logic;
+               RamClk:     out   std_logic;
+               RamCRE:     out   std_logic;
+               RamLB:      out   std_logic;
+               RamUB:      out   std_logic;
+               RamWait:    in    std_logic;
+
+               FlashRp:    out   std_logic;
+               FlashCS:    out   std_logic;
+               FlashStSts: in    std_logic;
+
+               MemAdr:     out   std_logic_vector(23 downto 1);
+               MemDB:      inout std_logic_vector(15 downto 0);
+
+               -- 7-segment display
+               seg:        out   std_logic_vector(6 downto 0);
+               dp:         out   std_logic;
+               an:         out   std_logic_vector(3 downto 0);
+
+               -- Misc
+               Led:        out   std_logic_vector(7 downto 0);
+               sw:         in    std_logic_vector(7 downto 0)
+       );
+end nexys2;
+
+
+architecture behavioral of nexys2 is
+
+       type regs8 is array(natural range <>) of std_logic_vector(7 downto 0);
+
+       -- Wishbone SYSCON signals
+       signal clk:            std_logic;
+       signal rst:            std_logic;
+
+begin
+
+       -- Wishbone SYSCON
+       clk <= clk_50;
+       rst <= sw(7);
+
+       e_host_ctrl: entity nexys2_lib.host_ctrl
+               port map (
+                       clk_i        => clk,
+                       rst_i        => rst,
+
+                       d_rst_o      => open,
+                       d_flags_o    => open,
+                       debug_i      => x"8877665544332211",
+                       debug_o      => open,
+
+                       d_cyc_i      => '0',
+                       d_stb_i      => '0',
+                       d_we_i       => '0',
+                       d_ack_o      => open,
+                       d_adr_i      => (others => '0'),
+                       d_dat_i      => (others => '0'),
+                       d_dat_o      => open,
+
+                       d_MemOE      => '1',
+                       d_MemWR      => '1',
+                       d_RamAdv     => '1',
+                       d_RamCS      => '1',
+                       d_RamClk     => '0',
+                       d_RamCRE     => '0',
+                       d_RamLB      => '1',
+                       d_RamUB      => '1',
+                       d_RamWait    => open,
+                       d_FlashRp    => '1',
+                       d_FlashCS    => '1',
+                       d_FlashStSts => open,
+                       d_MemAdr     => (others => '0'),
+                       d_MemDB_i    => open,
+                       d_MemDB_o    => (others => '0'),
+
+                       EppAstb      => EppAstb,
+                       EppDstb      => EppDstb,
+                       EppWr        => EppWRITE,
+                       EppDB        => DB,
+                       EppWait      => EppWait,
+
+                       MemOE        => MemOE,
+                       MemWR        => MemWR,
+                       RamAdv       => RamAdv,
+                       RamCS        => RamCS,
+                       RamClk       => RamClk,
+                       RamCRE       => RamCRE,
+                       RamLB        => RamLB,
+                       RamUB        => RamUB,
+                       RamWait      => RamWait,
+                       FlashRp      => FlashRp,
+                       FlashCS      => FlashCS,
+                       FlashStSts   => FlashStSts,
+                       MemAdr       => MemAdr,
+                       MemDB        => MemDB,
+
+                       seg          => seg,
+                       dp           => dp,
+                       an           => an,
+                       Led          => Led,
+                       sw           => sw
+               );
+
+end behavioral;
diff --git a/projects/nexys2_host_controller/testbench_hostctrl.vhd b/projects/nexys2_host_controller/testbench_hostctrl.vhd
new file mode 100644 (file)
index 0000000..e191a27
--- /dev/null
@@ -0,0 +1,208 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+library simulated;
+
+library work;
+
+entity testbench_hostctrl is
+end testbench_hostctrl;
+
+architecture behavior of testbench_hostctrl is
+
+       constant clk_50_period : time := 20 ns;
+
+       signal clk_50:     std_logic := '0';
+
+       signal EppASTB:    std_logic := '0';
+       signal EppDSTB:    std_logic := '0';
+       signal EppWRITE:   std_logic := '0';
+       signal EppWAIT:    std_logic;
+       signal EppDB:      std_logic_vector(7 downto 0);
+
+       signal RamWait:    std_logic := '0';
+       signal FlashStSts: std_logic := '0';
+
+       signal RamAdv:     std_logic;
+       signal RamCS:      std_logic;
+       signal RamClk:     std_logic;
+       signal RamCRE:     std_logic;
+       signal RamLB:      std_logic;
+       signal RamUB:      std_logic;
+
+       signal FlashRp:    std_logic;
+       signal FlashCS:    std_logic;
+
+       signal MemOE:      std_logic;
+       signal MemWR:      std_logic;
+       signal MemAdr:     std_logic_vector(23 downto 1);
+       signal MemDB:      std_logic_vector(15 downto 0);
+
+       signal sw:         std_logic_vector(7 downto 0) := (others => '0');
+       signal Led:        std_logic_vector(7 downto 0);
+
+begin
+
+       test: process
+       begin
+               -- Initial values
+               EppDB    <= (others => 'Z');
+               EppASTB  <= '1';
+               EppDSTB  <= '1';
+               EppWRITE <= '1';
+
+               -- Reset switch
+               sw <= (7 => '1', others => '0');
+               wait for 100 ns;
+               sw <= (others => '0');
+
+               -- 0x10 <= 0xa5
+               EppDB    <= x"10";
+               EppASTB  <= '0';
+               EppWRITE <= '0';
+               wait until EppWAIT = '1';
+               eppDB    <= (others => 'Z');
+               EppASTB  <= '1';
+               EppWRITE <= '1';
+               wait until EppWAIT = '0';
+               EppDB    <= x"a5";
+               EppDSTB  <= '0';
+               EppWRITE <= '0';
+               wait until EppWAIT = '1';
+               EppDB    <= (others => 'Z');
+               EppDSTB  <= '1';
+               EppWRITE <= '1';
+               wait until EppWAIT = '0';
+
+               -- 0x00 <= 0xc0
+               -- Enable OOB memory controller and reset device
+               EppDB    <= x"00";
+               EppASTB  <= '0';
+               EppWRITE <= '0';
+               wait until EppWAIT = '1';
+               EppDB    <= (others => 'Z');
+               EppASTB  <= '1';
+               EppWRITE <= '1';
+               wait until EppWAIT = '0';
+               EppDB    <= x"c0";
+               EppDSTB  <= '0';
+               EppWRITE <= '0';
+               wait until EppWAIT = '1';
+               EppDB    <= (others => 'Z');
+               EppDSTB  <= '1';
+               EppWRITE <= '1';
+               wait until EppWAIT = '0';
+
+               -- 0x08 <= 0x08
+               -- Enable flash and disable ram
+               EppDB    <= x"08";
+               EppASTB  <= '0';
+               EppWRITE <= '0';
+               wait until EppWAIT = '1';
+               EppDB    <= (others => 'Z');
+               EppASTB  <= '1';
+               EppWRITE <= '1';
+               wait until EppWAIT = '0';
+               EppDB    <= x"08";
+               EppDSTB  <= '0';
+               EppWRITE <= '0';
+               wait until EppWAIT = '1';
+               EppDB    <= (others => 'Z');
+               EppDSTB  <= '1';
+               EppWRITE <= '1';
+               wait until EppWAIT = '0';
+
+               -- 0x0c <= 0x98
+               -- CFI query command
+               EppDB    <= x"0c";
+               EppASTB  <= '0';
+               EppWRITE <= '0';
+               wait until EppWAIT = '1';
+               EppDB    <= (others => 'Z');
+               EppASTB  <= '1';
+               EppWRITE <= '1';
+               wait until EppWAIT = '0';
+               EppDB    <= x"98";
+               EppDSTB  <= '0';
+               EppWRITE <= '0';
+               wait until EppWAIT = '1';
+               EppDB    <= (others => 'Z');
+               EppDSTB  <= '1';
+               EppWRITE <= '1';
+               wait until EppWAIT = '0';
+
+               wait;
+       end process;
+
+
+       uut: entity work.nexys2
+               port map (
+                       clk_50     => clk_50,
+                       EppASTB    => EppASTB,
+                       EppDSTB    => EppDSTB,
+                       EppWRITE   => EppWRITE,
+                       EppWAIT    => EppWAIT,
+                       DB         => EppDB,
+                       MemOE      => MemOE,
+                       MemWR      => MemWR,
+                       RamAdv     => RamAdv,
+                       RamCS      => RamCS,
+                       RamClk     => RamClk,
+                       RamCRE     => RamCRE,
+                       RamLB      => RamLB,
+                       RamUB      => RamUB,
+                       RamWait    => RamWait,
+                       FlashRp    => FlashRp,
+                       FlashCS    => FlashCS,
+                       FlashStSts => FlashStSts,
+                       MemAdr     => MemAdr,
+                       MemDB      => MemDB,
+                       seg        => open,
+                       dp         => open,
+                       an         => open,
+                       Led        => Led,
+                       sw         => sw
+               );
+
+
+       ram: entity simulated.mt45w8mw16bgx
+               port map (
+                       a     => MemAdr,
+                       dq    => MemDB,
+                       clk   => RamClk,
+                       adv_n => RamAdv,
+                       cre   => RamCRE,
+                       ce_n  => RamCS,
+                       oe_n  => MemOE,
+                       we_n  => MemWR,
+                       lb_n  => RamLB,
+                       ub_n  => RamUB,
+                       rwait => RamWait
+               );
+
+
+       flash: entity simulated.js28f128j3d75
+               port map (
+                       a      => MemAdr & '0',
+                       d      => MemDB,
+                       ce     => "00" & FlashCS,
+                       rp_n   => FlashRp,
+                       oe_n   => MemOE,
+                       we_n   => MemWR,
+                       sts    => FlashStSts,
+                       byte_n => '1',
+                       vpen   => '1'
+               );
+
+
+       clk_50_process: process
+       begin
+               clk_50 <= '0';
+               wait for clk_50_period / 2;
+               clk_50 <= '1';
+               wait for clk_50_period / 2;
+       end process;
+
+end;