--- /dev/null
+*.bit
+*.bin
+
+# Xilinx ISE license file
+*.lic
+
+# The UCF files for the Nexys2 come from Digilent, and aren't mine to distribute
+*.ucf
+
+.idea/
--- /dev/null
+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;
--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f test_nexys2_mem_wb8_0.bit prog
+
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;
--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;
--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;
--- /dev/null
+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;
--- /dev/null
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog
+#djtgcfg -d Nexys2 -i 0 -f OnBoardMemCfg.bit prog
+
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;
--- /dev/null
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f test_nexys2.bit prog
+
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
+
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+-- 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;
--- /dev/null
+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;
--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f test_nexys2.bit prog
+
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+#!/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())
--- /dev/null
+( 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
--- /dev/null
+( 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
--- /dev/null
+--------------------------------------------------------------------------------
+--
+-- +---+ +---+ +---+ +---+ +---+
+-- | 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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog
+
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+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;
--- /dev/null
+#!/usr/bin/env bash
+
+djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog
+#djtgcfg -d Nexys2 -i 0 -f OnBoardMemCfg.bit prog
+
--- /dev/null
+#!/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
+
--- /dev/null
+#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
+}
+
--- /dev/null
+#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");
+}
+
--- /dev/null
+/*
+
+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);
+
--- /dev/null
+#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
+}
+
--- /dev/null
+#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
+}
+
--- /dev/null
+#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
+}
+
--- /dev/null
+#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);
+
--- /dev/null
+#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, ®_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
+}
+
--- /dev/null
+#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);
+
--- /dev/null
+#!/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
+
--- /dev/null
+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;
--- /dev/null
+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;