From: rs <> Date: Sat, 28 Jun 2025 03:19:34 +0000 (-0500) Subject: Initial commit X-Git-Url: https://git.the-white-hart.net/?a=commitdiff_plain;h=83b53de3954561eabe0116a95c91becbf03017c2;p=vhdl Initial commit --- 83b53de3954561eabe0116a95c91becbf03017c2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1598bd3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.bit +*.bin + +# Xilinx ISE license file +*.lic + +# The UCF files for the Nexys2 come from Digilent, and aren't mine to distribute +*.ucf + +.idea/ diff --git a/libraries/nexys2/epp_wb.vhd b/libraries/nexys2/epp_wb.vhd new file mode 100644 index 0000000..a854496 --- /dev/null +++ b/libraries/nexys2/epp_wb.vhd @@ -0,0 +1,192 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity epp_wb is + port ( + -- Epp-bus signals + EppAstb: in std_logic; + EppDstb: in std_logic; + EppWr: in std_logic; + EppDB: inout std_logic_vector(7 downto 0); + EppWait: out std_logic; + + -- Wishbone bus signals + rst_i: in std_logic; + clk_i: in std_logic; + adr_o: out std_logic_vector(7 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + cyc_o: out std_logic; + stb_o: out std_logic; + we_o: out std_logic; + ack_i: in std_logic + ); +end epp_wb; + + +architecture behavioral of epp_wb is + + type state_t is ( + S_READY, + S_ADDR, + S_DATA, + S_DONE + ); + + -- Registered epp bus signals + signal epp_astb_reg: std_logic; + signal epp_dstb_reg: std_logic; + signal epp_wr_reg: std_logic; + signal epp_data_in_reg: std_logic_vector(7 downto 0); + signal epp_data_out_reg: std_logic_vector(7 downto 0); + + signal epp_data_out_latch: std_logic; + signal epp_data_out_en: std_logic; + + -- Internal address register + signal addr_latch: std_logic; + signal addr_reg: std_logic_vector(7 downto 0); + + -- Registered wishbone bus signals + signal next_stb: std_logic; + signal stb_reg: std_logic; + signal next_we: std_logic; + signal we_reg: std_logic; + + -- State machine + signal cur_state_reg: state_t; + signal next_state: state_t; + +begin + + -- Interface output should be in tristate until data is available for EPP to read + EppDB <= epp_data_out_reg when epp_data_out_en = '1' else (others => 'Z'); + + -- Output registered wishbone signals + cyc_o <= stb_reg; + stb_o <= stb_reg; + we_o <= we_reg; + + -- Always present the latched EPP data bus and latched address to the wishbone bus + dat_o <= epp_data_in_reg; + adr_o <= addr_reg; + + -- State register + p_regs: process (clk_i, rst_i, next_state) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + cur_state_reg <= S_READY; + else + cur_state_reg <= next_state; + end if; + end if; + end process; + + -- Regiter EPP bus inputs to break combinational chain with the outside world + p_epp_reg: process (clk_i, EppAstb, EppDstb, EppWr, EppDB) + begin + if rising_edge(clk_i) then + epp_astb_reg <= EppAstb; + epp_dstb_reg <= EppDstb; + epp_wr_reg <= EppWr; + epp_data_in_reg <= EppDB; + end if; + end process; + + -- Register wishbone outputs + p_wb_reg: process (clk_i, rst_i, next_stb, next_we) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + stb_reg <= '0'; + we_reg <= '0'; + else + stb_reg <= next_stb; + we_reg <= next_we; + end if; + end if; + end process; + + -- Address register + p_addr: process (clk_i, addr_latch, epp_data_in_reg) + begin + if rising_edge(clk_i) and addr_latch = '1' then + addr_reg <= epp_data_in_reg; + end if; + end process; + + -- Data register from wishbone bus to epp bus + p_data: process (clk_i, epp_data_out_latch, dat_i) + begin + if rising_edge(clk_i) and epp_data_out_latch = '1' then + epp_data_out_reg <= dat_i; + end if; + end process; + + -- State machine transitions + p_fsm: process(cur_state_reg, + epp_astb_reg, epp_dstb_reg, epp_wr_reg, + ack_i) + begin + -- Default to remaining in same state + next_state <= cur_state_reg; + + -- Default to not starting a wishbone cycle + next_stb <= '0'; + next_we <= '0'; + + -- Default to not latching or outputting anything + addr_latch <= '0'; + epp_data_out_latch <= '0'; + epp_data_out_en <= '0'; + + -- Default to telling EPP-side to wait + EppWait <= '0'; + + case cur_state_reg is + when S_READY => + -- Leave state when a strobe is asserted + if epp_astb_reg = '0' then + next_state <= S_ADDR; + elsif epp_dstb_reg = '0' then + next_stb <= '1'; + next_we <= not epp_wr_reg; + next_state <= S_DATA; + end if; + + when S_DATA => + -- Ack may be combinational, so don't register it + if ack_i = '1' then + -- Latch data from wishbone, as EPP may take more time to read + epp_data_out_latch <= '1'; + next_state <= S_DONE; + else + -- Continue asserting bus signals next cycle if not acknowledged + next_stb <= '1'; + next_we <= not epp_wr_reg; + end if; + + when S_ADDR => + addr_latch <= '1'; + next_state <= S_DONE; -- No stretcing will occur for address access + + when S_DONE => + -- Indicate completion + EppWait <= '1'; + + -- If a read is being performed, take the EPP bus out of tristate + epp_data_out_en <= epp_wr_reg; + + -- Wait for strobes to be deasserted before returning to READY state + if epp_astb_reg = '1' and epp_dstb_reg = '1' then + next_state <= S_READY; + end if; + + when others => + next_state <= S_READY; + end case; + end process; + +end behavioral; diff --git a/libraries/nexys2/host_ctrl.vhd b/libraries/nexys2/host_ctrl.vhd new file mode 100644 index 0000000..5839450 --- /dev/null +++ b/libraries/nexys2/host_ctrl.vhd @@ -0,0 +1,326 @@ +-------------------------------------------------------------------------------- +-- Registers: +-- +-- 0: CTRL +-- 1: MBOX_FLAGS +-- 2: MBOX_VALUE +-- 3: SW(R)/LED(W) +-- 4: SSEG0 +-- 5: SSEG1 +-- 6: SSEG2 +-- 7: SSEG3 +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library utility; +library work; + + +entity host_ctrl is + port ( + -- Wishbone global signals + clk_i: in std_logic; + rst_i: in std_logic; + + -- Signals to device + d_rst_o: out std_logic; + d_flags_o: out std_logic_vector(7 downto 0); + debug_i: in std_logic_vector(63 downto 0); + debug_o: out std_logic_vector(63 downto 0); + + -- Device-side wishbone bus slave + d_cyc_i: in std_logic; + d_stb_i: in std_logic; + d_we_i: in std_logic; + d_ack_o: out std_logic; + d_adr_i: in std_logic_vector(2 downto 0); + d_dat_i: in std_logic_vector(7 downto 0); + d_dat_o: out std_logic_vector(7 downto 0); + + -- Device memory interface + d_MemOE: in std_logic; + d_MemWR: in std_logic; + d_RamAdv: in std_logic; + d_RamCS: in std_logic; + d_RamClk: in std_logic; + d_RamCRE: in std_logic; + d_RamLB: in std_logic; + d_RamUB: in std_logic; + d_RamWait: out std_logic; + d_FlashRp: in std_logic; + d_FlashCS: in std_logic; + d_FlashStSts: out std_logic; + d_MemAdr: in std_logic_vector(23 downto 1); + d_MemDB_o: in std_logic_vector(15 downto 0); -- outbound - from device to memory + d_MemDB_i: out std_logic_vector(15 downto 0); -- inbound - from memory to device + + -- Nexys2 IO pins + EppAstb: in std_logic; + EppDstb: in std_logic; + EppWr: in std_logic; + EppDB: inout std_logic_vector(7 downto 0); + EppWait: out std_logic; + + MemOE: out std_logic; + MemWR: out std_logic; + RamAdv: out std_logic; + RamCS: out std_logic; + RamClk: out std_logic; + RamCRE: out std_logic; + RamLB: out std_logic; + RamUB: out std_logic; + RamWait: in std_logic; + FlashRp: out std_logic; + FlashCS: out std_logic; + FlashStSts: in std_logic; + MemAdr: out std_logic_vector(23 downto 1); + MemDB: inout std_logic_vector(15 downto 0); + + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + Led: out std_logic_vector(7 downto 0); + sw: in std_logic_vector(7 downto 0) + ); +end host_ctrl; + + +architecture behavioral of host_ctrl is + + -- Host Wishbone bus from EPP bridge + signal h_cyc: std_logic; + signal h_stb: std_logic; + signal h_we: std_logic; + signal h_ack: std_logic; + signal h_adr: std_logic_vector(7 downto 0); + signal h_dat_miso: std_logic_vector(7 downto 0); + signal h_dat_mosi: std_logic_vector(7 downto 0); + + signal h_cyc_ctl: std_logic; + signal h_dat_miso_ctl: std_logic_vector(7 downto 0); + signal h_ack_ctl: std_logic; + + signal h_cyc_oob: std_logic; + signal h_dat_miso_oob: std_logic_vector(7 downto 0); + signal h_ack_oob: std_logic; + + signal h_cyc_deb: std_logic; + signal h_dat_miso_deb: std_logic_vector(7 downto 0); + signal h_ack_deb: std_logic; + + -- Control register signals + signal ctrl: std_logic_vector(7 downto 0); + signal flags: std_logic_vector(7 downto 0); + + -- Out-of-band memory controller signals + signal oob_mem_oe: std_logic; + signal oob_mem_wr: std_logic; + signal oob_ram_adv: std_logic; + signal oob_ram_cs: std_logic; + signal oob_ram_clk: std_logic; + signal oob_ram_cre: std_logic; + signal oob_ram_ub: std_logic; + signal oob_ram_lb: std_logic; + signal oob_ram_wait: std_logic; + signal oob_flash_rp: std_logic; + signal oob_flash_cs: std_logic; + signal oob_flash_ststs: std_logic; + signal oob_mem_adr: std_logic_vector(23 downto 1); + signal oob_mem_db_i: std_logic_vector(15 downto 0); + signal oob_mem_db_o: std_logic_vector(15 downto 0); + +begin + + -- EPP to Wishbone bridge + e_epp_wb: entity work.epp_wb + port map ( + EppAstb => EppAstb, + EppDstb => EppDstb, + EppWr => EppWr, + EppDB => EppDB, + EppWait => EppWait, + + rst_i => rst_i, + clk_i => clk_i, + adr_o => h_adr, + dat_i => h_dat_miso, + dat_o => h_dat_mosi, + cyc_o => h_cyc, + stb_o => h_stb, + we_o => h_we, + ack_i => h_ack + ); + + + -- Host-side Wishbone multiplexer + p_wb_mux: process (h_adr, h_cyc, + h_dat_miso_ctl, h_ack_ctl, + h_dat_miso_oob, h_ack_oob) + begin + h_cyc_ctl <= '0'; + h_cyc_oob <= '0'; + + case h_adr(7 downto 3) is + when "00000" => + h_cyc_ctl <= h_cyc; + h_dat_miso <= h_dat_miso_ctl; + h_ack <= h_ack_ctl; + + when "00001" => + h_cyc_oob <= h_cyc; + h_dat_miso <= h_dat_miso_oob; + h_ack <= h_ack_oob; + + when "00010" => + h_cyc_deb <= h_cyc; + h_dat_miso <= h_dat_miso_deb; + h_ack <= h_ack_deb; + + when others => + h_dat_miso <= (others => '1'); + h_ack <= '1'; -- Eat to prevent deadlocks + end case; + end process; + + + -- Wishbone slave for control registers + e_host_regs: entity work.host_regs + port map ( + rst_i => rst_i, + clk_i => clk_i, + + h_cyc_i => h_cyc_ctl, + h_stb_i => h_stb, + h_we_i => h_we, + h_ack_o => h_ack_ctl, + h_adr_i => h_adr(2 downto 0), + h_dat_i => h_dat_mosi, + h_dat_o => h_dat_miso_ctl, + + d_cyc_i => d_cyc_i, + d_stb_i => d_stb_i, + d_we_i => d_we_i, + d_ack_o => d_ack_o, + d_adr_i => d_adr_i, + d_dat_i => d_dat_i, + d_dat_o => d_dat_o, + + ctrl => ctrl, + flags => d_flags_o, + seg => seg, + dp => dp, + an => an, + Led => Led, + sw => sw + ); + + -- Auxiliary signals to device + d_rst_o <= ctrl(7); + + + -- Wishbone slave for out-of-band memory controller + e_mem_oob: entity work.mem_wb_basic + port map ( + clk_i => clk_i, + rst_i => rst_i, + + cyc_i => h_cyc_oob, + stb_i => h_stb, + we_i => h_we, + ack_o => h_ack_oob, + adr_i => h_adr(2 downto 0), + dat_i => h_dat_mosi, + dat_o => h_dat_miso_oob, + + MemOE => oob_mem_oe, + MemWR => oob_mem_wr, + RamAdv => oob_ram_adv, + RamCS => oob_ram_cs, + RamClk => oob_ram_clk, + RamCRE => oob_ram_cre, + RamUB => oob_ram_ub, + RamLB => oob_ram_lb, + RamWait => oob_ram_wait, + FlashRp => oob_flash_rp, + FlashCS => oob_flash_cs, + FlashStSts => oob_flash_ststs, + MemAdr => oob_mem_adr, + MemDB_i => oob_mem_db_i, + MemDB_o => oob_mem_db_o + ); + + + -- Wishbone slave for debug reads + e_debug: entity utility.wb_debug + port map ( + rst_i => rst_i, + clk_i => clk_i, + cyc_i => h_cyc_deb, + stb_i => h_stb, + we_i => h_we, + ack_o => h_ack_deb, + adr_i => h_adr(2 downto 0), + dat_i => h_dat_mosi, + dat_o => h_dat_miso_deb, + debug_i => debug_i, + debug_o => debug_o + ); + + + -- Memory multiplexer + e_mem_mux: entity work.mem_mux + port map ( + sel => ctrl(6), + + s0_MemOE => d_MemOE, + s0_MemWR => d_MemWR, + s0_RamAdv => d_RamAdv, + s0_RamCS => d_RamCS, + s0_RamClk => d_RamClk, + s0_RamCRE => d_RamCRE, + s0_RamUB => d_RamUB, + s0_RamLB => d_RamLB, + s0_RamWait => d_RamWait, + s0_FlashRp => d_FlashRp, + s0_FlashCS => d_FlashCS, + s0_FlashStSts => d_FlashStSts, + s0_MemAdr => d_MemAdr, + s0_MemDB_i => d_MemDB_i, + s0_MemDB_o => d_MemDB_o, + + s1_MemOE => oob_mem_oe, + s1_MemWR => oob_mem_wr, + s1_RamAdv => oob_ram_adv, + s1_RamCS => oob_ram_cs, + s1_RamClk => oob_ram_clk, + s1_RamCRE => oob_ram_cre, + s1_RamUB => oob_ram_ub, + s1_RamLB => oob_ram_lb, + s1_RamWait => oob_ram_wait, + s1_FlashRp => oob_flash_rp, + s1_FlashCS => oob_flash_cs, + s1_FlashStSts => oob_flash_ststs, + s1_MemAdr => oob_mem_adr, + s1_MemDB_i => oob_mem_db_i, + s1_MemDB_o => oob_mem_db_o, + + MemOE => MemOE, + MemWR => MemWR, + RamAdv => RamAdv, + RamCS => RamCS, + RamClk => RamClk, + RamCRE => RamCRE, + RamUB => RamUB, + RamLB => RamLB, + RamWait => RamWait, + FlashRp => FlashRp, + FlashCS => FlashCS, + FlashStSts => FlashStSts, + MemAdr => MemAdr, + MemDB => MemDB + ); + +end behavioral; diff --git a/libraries/nexys2/host_regs.vhd b/libraries/nexys2/host_regs.vhd new file mode 100644 index 0000000..8ea6e6d --- /dev/null +++ b/libraries/nexys2/host_regs.vhd @@ -0,0 +1,128 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + + +entity host_regs is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + h_cyc_i: in std_logic; + h_stb_i: in std_logic; + h_we_i: in std_logic; + h_ack_o: out std_logic; + h_adr_i: in std_logic_vector(2 downto 0); + h_dat_i: in std_logic_vector(7 downto 0); + h_dat_o: out std_logic_vector(7 downto 0); + + d_cyc_i: in std_logic; + d_stb_i: in std_logic; + d_we_i: in std_logic; + d_ack_o: out std_logic; + d_adr_i: in std_logic_vector(2 downto 0); + d_dat_i: in std_logic_vector(7 downto 0); + d_dat_o: out std_logic_vector(7 downto 0); + + ctrl: out std_logic_vector(7 downto 0); + flags: out std_logic_vector(7 downto 0); + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + Led: out std_logic_vector(7 downto 0); + sw: in std_logic_vector(7 downto 0) + ); +end host_regs; + + +architecture behavioral of host_regs is + + type byte_array is array(natural range <>) of std_logic_vector(7 downto 0); + + signal regs: byte_array(7 downto 0); + + signal clk_div: unsigned(15 downto 0); + signal temp_an: std_logic_vector(3 downto 0); + +begin + + h_ack_o <= '1'; + h_dat_o <= sw when h_adr_i = "011" + else regs(to_integer(unsigned(h_adr_i))); + + d_ack_o <= '0' when h_cyc_i = '1' and h_we_i = '1' else '1'; + d_dat_o <= sw when d_adr_i = "011" + else regs(to_integer(unsigned(d_adr_i))); + + process (clk_i, rst_i, + h_cyc_i, h_stb_i, h_we_i, h_adr_i, h_dat_i, + d_cyc_i, d_stb_i, d_we_i, d_adr_i, d_dat_i, + sw) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + -- Reset + regs(0) <= (7 => '1', others => '0'); + regs(1) <= (others => '0'); + regs(2) <= (others => '0'); + regs(3) <= (others => '0'); + regs(4) <= (others => '1'); + regs(5) <= (others => '1'); + regs(6) <= (others => '1'); + regs(7) <= (others => '1'); + else + if h_cyc_i = '1' and h_stb_i = '1' then + if h_we_i = '1' then + -- Host write + case h_adr_i is + when "001" => regs(1) <= regs(1) or h_dat_i; -- Register 1 is write-one-set + when others => regs(to_integer(unsigned(h_adr_i))) <= h_dat_i; + end case; + end if; + elsif d_cyc_i = '1' and d_stb_i = '1' then + if d_we_i = '1' then + -- Device write + case d_adr_i(2 downto 0) is + when "000" => null; -- Register 0 is read-only + when "001" => regs(1) <= regs(1) and not d_dat_i; -- Register 1 is write-one-clear + when others => regs(to_integer(unsigned(d_adr_i))) <= d_dat_i; + end case; + end if; + end if; + end if; + end if; + end process; + + ctrl <= regs(0); + flags <= regs(1); + + -- LEDs + Led <= regs(3); + + process (clk_i, clk_div) + begin + if rising_edge(clk_i) then + clk_div <= clk_div + 1; + end if; + end process; + + temp_an <= regs(7)(7) & regs(6)(7) & regs(5)(7) & regs(4)(7); + + e_seven_seg_mux: entity work.seven_seg_mux + port map ( + clk_in => clk_div(15), + + seg_0_in => regs(4)(6 downto 0), + seg_1_in => regs(5)(6 downto 0), + seg_2_in => regs(6)(6 downto 0), + seg_3_in => regs(7)(6 downto 0), + dps_in => temp_an, + + seg_out => seg, + dp_out => dp, + an_out => an + ); + +end behavioral; diff --git a/libraries/nexys2/mem_mux.vhd b/libraries/nexys2/mem_mux.vhd new file mode 100644 index 0000000..acde130 --- /dev/null +++ b/libraries/nexys2/mem_mux.vhd @@ -0,0 +1,91 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity mem_mux is + port ( + sel: in std_logic; + + s0_MemOE: in std_logic; + s0_MemWR: in std_logic; + s0_RamAdv: in std_logic; + s0_RamCS: in std_logic; + s0_RamClk: in std_logic; + s0_RamCRE: in std_logic; + s0_RamUB: in std_logic; + s0_RamLB: in std_logic; + s0_RamWait: out std_logic; + s0_FlashRp: in std_logic; + s0_FlashCS: in std_logic; + s0_FlashStSts: out std_logic; + s0_MemAdr: in std_logic_vector(23 downto 1); + s0_MemDB_i: out std_logic_vector(15 downto 0); -- Inbound: from memory to device + s0_MemDB_o: in std_logic_vector(15 downto 0); -- Outbound: from device to memory + + s1_MemOE: in std_logic; + s1_MemWR: in std_logic; + s1_RamAdv: in std_logic; + s1_RamCS: in std_logic; + s1_RamClk: in std_logic; + s1_RamCRE: in std_logic; + s1_RamUB: in std_logic; + s1_RamLB: in std_logic; + s1_RamWait: out std_logic; + s1_FlashRp: in std_logic; + s1_FlashCS: in std_logic; + s1_FlashStSts: out std_logic; + s1_MemAdr: in std_logic_vector(23 downto 1); + s1_MemDB_i: out std_logic_vector(15 downto 0); -- Inbound: from memory to device + s1_MemDB_o: in std_logic_vector(15 downto 0); -- Outbound: from device to memory + + MemOE: out std_logic; + MemWR: out std_logic; + RamAdv: out std_logic; + RamCS: out std_logic; + RamClk: out std_logic; + RamCRE: out std_logic; + RamUB: out std_logic; + RamLB: out std_logic; + RamWait: in std_logic; + FlashRp: out std_logic; + FlashCS: out std_logic; + FlashStSts: in std_logic; + MemAdr: out std_logic_vector(23 downto 1); + MemDB: inout std_logic_vector(15 downto 0) + ); +end mem_mux; + + +architecture behavioral of mem_mux is + + --signal oe: std_logic; + signal we: std_logic; + +begin + + MemOE <= s0_MemOE when sel = '0' else s1_MemOE; + MemWR <= s0_MemWR when sel = '0' else s1_MemWR; + RamAdv <= s0_RamAdv when sel = '0' else s1_RamAdv; + RamCS <= s0_RamCS when sel = '0' else s1_RamCS; + RamClk <= s0_RamClk when sel = '0' else s1_RamClk; + RamCRE <= s0_RamCRE when sel = '0' else s1_RamCRE; + RamUB <= s0_RamUB when sel = '0' else s1_RamUB; + RamLB <= s0_RamLB when sel = '0' else s1_RamLB; + FlashRp <= s0_FlashRp when sel = '0' else s1_FlashRp; + FlashCS <= s0_FlashCS when sel = '0' else s1_FlashCS; + MemAdr <= s0_MemAdr when sel = '0' else s1_MemAdr; + + --oe <= s0_MemOE when sel = '0' else s1_MemOE; + we <= s0_MemWR when sel = '0' else s1_MemWR; + MemDB <= (others => 'Z') when we = '1' else + s0_MemDB_o when sel = '0' else s1_MemDB_o; + + s0_RamWait <= RamWait; + s0_FlashStSts <= FlashStSts; + s0_MemDB_i <= MemDB; + + s1_RamWait <= RamWait; + s1_FlashStSts <= FlashStSts; + s1_MemDB_i <= MemDB; + +end behavioral; diff --git a/libraries/nexys2/mem_wb8_0.vhd b/libraries/nexys2/mem_wb8_0.vhd new file mode 100644 index 0000000..45d9003 --- /dev/null +++ b/libraries/nexys2/mem_wb8_0.vhd @@ -0,0 +1,149 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + + +entity mem_wb8_0 is + port ( + -- Wishbone SYSCON + rst_i: in std_logic; + clk_i: in std_logic; + + -- Wishbone system interface + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(24 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + -- Configuration + wait_cycles: in std_logic_vector(4 downto 0); + + -- Memory interface + MemOE: out std_logic; + MemWR: out std_logic; + RamAdv: out std_logic; + RamCS: out std_logic; + RamClk: out std_logic; + RamCRE: out std_logic; + RamUB: out std_logic; + RamLB: out std_logic; + RamWait: in std_logic; + FlashRp: out std_logic; + FlashCS: out std_logic; + FlashStSts: in std_logic; + MemAdr: out std_logic_vector(23 downto 1); + MemDB_i: in std_logic_vector(15 downto 0); -- Inbound: from memory to device + MemDB_o: out std_logic_vector(15 downto 0) -- Outbound: from device to memory + ); +end mem_wb8_0; + + +architecture behavioral of mem_wb8_0 is + + type state_t is (S_IDLE, S_WAIT, S_DONE); + + signal state_reg: state_t; + signal state_next: state_t; + + signal wait_count_reg: unsigned(4 downto 0); + signal wait_reset: std_logic; + signal wait_done: std_logic; + + signal mem_enable: std_logic; + + signal mdr_reg: std_logic_vector(15 downto 0); + signal mdr_ld: std_logic; + +begin + + process (rst_i, clk_i, state_next) + begin + if rising_edge(clk_i) then + state_reg <= state_next; + end if; + end process; + + process (state_reg, cyc_i, stb_i, we_i, wait_done) + begin + state_next <= state_reg; + wait_reset <= '1'; + mem_enable <= '0'; + ack_o <= '0'; + MemOE <= '1'; + MemWR <= '1'; + mdr_ld <= '0'; + + case state_reg is + when S_IDLE => + -- Returning to this state for one clock cycle between ram + -- writes gives enough time for the ram to self-refresh, at + -- least at 50 MHz - more inactive cycles are needed if faster + -- (Ram CE must be high 15us every 4us) + if cyc_i = '1' and stb_i = '1' then + state_next <= S_WAIT; + end if; + + when S_WAIT => + wait_reset <= '0'; + mem_enable <= '1'; + MemOE <= we_i; + MemWR <= not we_i; + if wait_done = '1' then + --ack_o <= '1'; + mdr_ld <= '1'; + --state_next <= S_IDLE; + state_next <= S_DONE; + end if; + + when S_DONE => + ack_o <= '1'; + state_next <= S_IDLE; + + when others => + state_next <= S_IDLE; + end case; + end process; + + -- Little-endian memory interface + RamCS <= not (mem_enable and adr_i(24)); + RamAdv <= '0'; + RamClk <= '0'; + RamCRE <= '0'; + RamUB <= not adr_i(0); + RamLB <= adr_i(0); + FlashCS <= not (mem_enable and (not adr_i(24))); + FlashRp <= '1'; + MemAdr <= adr_i(23 downto 1); + MemDB_o(15 downto 8) <= dat_i when adr_i(0) = '1' else (others => '0'); + MemDB_o( 7 downto 0) <= dat_i; + --dat_o <= MemDB_i(15 downto 8) when adr_i(0) = '1' else MemDB_i(7 downto 0); + dat_o <= mdr_reg(15 downto 8) when adr_i(0) = '1' else mdr_reg(7 downto 0); + + -- Wait cycle counter + process (rst_i, clk_i, wait_reset, wait_count_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' or wait_reset = '1' then + wait_count_reg <= unsigned(wait_cycles); + elsif wait_done = '0' then + wait_count_reg <= wait_count_reg - 1; + end if; + end if; + end process; + wait_done <= not or_reduce(std_logic_vector(wait_count_reg)); + + -- Memory data register + process (clk_i, mdr_ld) + begin + if rising_edge(clk_i) then + if mdr_ld = '1' then + mdr_reg <= MemDB_i; + end if; + end if; + end process; + +end behavioral; diff --git a/libraries/nexys2/mem_wb_basic.vhd b/libraries/nexys2/mem_wb_basic.vhd new file mode 100644 index 0000000..cb9531b --- /dev/null +++ b/libraries/nexys2/mem_wb_basic.vhd @@ -0,0 +1,227 @@ +-------------------------------------------------------------------------------- +-- Registers: +-- +-- +---+---+---+---+---+---+---+---+ +-- | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +-- +---+---+---+---+---+---+---+---+ +-- 0: |FLS|FLS| | | |RAM|RAM|AUT| CTRL +-- |RST|CS | | | |CS |CRE|CNT| +-- +---+---+---+---+---+---+---+---+ +-- 1: | ADDR_L | A(0) IGNORED +-- +-------------------------------+ +-- 2: | ADDR_M | +-- +-------------------------------+ +-- 3: | ADDR_H | +-- +-------------------------------+ +-- 4: | DATA_L | TRIGGER +-- +-------------------------------+ +-- 5: | DATA_H | +-- +-------------------------------+ +-- 6: | (RESERVED) | +-- +-------------------------------+ +-- 7: | (RESERVED) | +-- +-------------------------------+ +-- +-------------------------------------------------------------------------------- + + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity mem_wb_basic is + port ( + -- Global wishbone signals + clk_i: in std_logic; + rst_i: in std_logic; + + -- Wishbone bus signals + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(2 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + -- Memory interface signals + MemOE: out std_logic; + MemWR: out std_logic; + RamAdv: out std_logic; + RamCS: out std_logic; + RamClk: out std_logic; + RamCRE: out std_logic; + RamUB: out std_logic; + RamLB: out std_logic; + RamWait: in std_logic; + FlashRp: out std_logic; + FlashCS: out std_logic; + FlashStSts: in std_logic; + MemAdr: out std_logic_vector(23 downto 1); + MemDB_i: in std_logic_vector(15 downto 0); -- Inbound: from memory to device + MemDB_o: out std_logic_vector(15 downto 0) -- Outbound: from device to memory + ); +end mem_wb_basic; + + +architecture behavioral of mem_wb_basic is + + type state is (S_READY, S_WAIT_0, S_WAIT_1, S_WAIT_2, S_WAIT_3, S_WAIT_4, S_WAIT_5, S_DONE); + + -- CTRL register bits: + -- 2 - ram_cre + -- 3 - ram_cs + -- 6 - flash_cs + -- 7 - flash_rp + signal ctrl_reg: std_logic_vector( 7 downto 0); + signal addr_reg: std_logic_vector(23 downto 0); + signal data_reg: std_logic_vector(15 downto 0); + + signal ctrl_fls_rst: std_logic; + signal ctrl_fls_cs: std_logic; + signal ctrl_ram_cs: std_logic; + signal ctrl_ram_cre: std_logic; + signal ctrl_aut_cnt: std_logic; + + signal cur_state_reg: state; + signal next_state: state; + signal data_latch: std_logic; + signal addr_inc: std_logic; + +begin + + -- Control signals + ctrl_fls_rst <= ctrl_reg(7); + ctrl_fls_cs <= ctrl_reg(6); + ctrl_ram_cs <= ctrl_reg(2); + ctrl_ram_cre <= ctrl_reg(1); + ctrl_aut_cnt <= ctrl_reg(0); + + -- Memory signals + RamAdv <= '0'; + RamCS <= ctrl_ram_cs or not cyc_i; -- Gate RAM CS to allow refresh + RamClk <= '0'; + RamCRE <= ctrl_ram_cre; + RamUB <= '0'; + RamLB <= '0'; + FlashRp <= ctrl_fls_rst; + FlashCS <= ctrl_fls_cs; + MemAdr <= addr_reg(23 downto 1); + MemDB_o <= data_reg; + + -- Wishbone signals + with adr_i select dat_o <= + ctrl_reg when "000", + addr_reg( 7 downto 0) when "001", + addr_reg(15 downto 8) when "010", + addr_reg(23 downto 16) when "011", + data_reg( 7 downto 0) when "100", + data_reg(15 downto 8) when "101", + (others => '1') when others; + + -- Wishbone bus + process (clk_i, rst_i, + cyc_i, stb_i, we_i, adr_i, dat_i, + cur_state_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + ctrl_reg <= "11001000"; + addr_reg <= (others => '0'); + data_reg <= (others => '0'); + cur_state_reg <= S_READY; + else + cur_state_reg <= next_state; + + if ctrl_aut_cnt = '1' and addr_inc = '1' then + addr_reg <= std_logic_vector(unsigned(addr_reg) + 2); + end if; + + if cyc_i = '1' and stb_i = '1' and we_i = '1' then + case adr_i is + when "000" => ctrl_reg <= dat_i; + when "001" => addr_reg( 7 downto 0) <= dat_i; + when "010" => addr_reg(15 downto 8) <= dat_i; + when "011" => addr_reg(23 downto 16) <= dat_i; + when "100" => data_reg( 7 downto 0) <= dat_i; + when "101" => data_reg(15 downto 8) <= dat_i; + when others => null; + end case; + end if; + + if data_latch = '1' then + data_reg <= MemDB_i; + end if; + end if; + end if; + end process; + + process (cur_state_reg, + cyc_i, stb_i, we_i, adr_i) + begin + next_state <= cur_state_reg; + data_latch <= '0'; + addr_inc <= '0'; + ack_o <= '0'; -- TODO: test with this removed if it stops working + + MemOE <= '1'; + MemWR <= '1'; + + case cur_state_reg is + when S_READY => + if adr_i = "100" then + -- Reads/writes to DATA_LO trigger memory cycles, + -- which take longer than one clock cycle + ack_o <= '0'; + if cyc_i = '1' and stb_i = '1' then + next_state <= S_WAIT_0; + end if; + else + ack_o <= '1'; + end if; + + when S_WAIT_0 => + MemOE <= we_i; + MemWR <= not we_i; + next_state <= S_WAIT_1; + + when S_WAIT_1 => + MemOE <= we_i; + MemWR <= not we_i; + next_state <= S_WAIT_2; + + when S_WAIT_2 => + MemOE <= we_i; + MemWR <= not we_i; + next_state <= S_WAIT_3; + + when S_WAIT_3 => + MemOE <= we_i; + MemWR <= not we_i; + next_state <= S_WAIT_4; + + when S_WAIT_4 => + MemOE <= we_i; + MemWR <= not we_i; + next_state <= S_WAIT_5; + + when S_WAIT_5 => + MemOE <= we_i; + MemWR <= not we_i; + data_latch <= not we_i; + next_state <= S_DONE; + + when S_DONE => + MemOE <= we_i; + MemWR <= '1'; + ack_o <= '1'; + addr_inc <= '1'; + next_state <= S_READY; + + when others => + next_state <= S_READY; + end case; + end process; + +end behavioral; diff --git a/libraries/nexys2/seven_seg_hex.vhd b/libraries/nexys2/seven_seg_hex.vhd new file mode 100644 index 0000000..04b2c77 --- /dev/null +++ b/libraries/nexys2/seven_seg_hex.vhd @@ -0,0 +1,36 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity seven_seg_hex is + port ( + data_in: in std_logic_vector(3 downto 0); + seg_out: out std_logic_vector(6 downto 0) + ); +end seven_seg_hex; + + +architecture behavioral of seven_seg_hex is +begin + + with data_in select seg_out <= + "1000000" when "0000", + "1111001" when "0001", + "0100100" when "0010", + "0110000" when "0011", + "0011001" when "0100", + "0010010" when "0101", + "0000010" when "0110", + "1111000" when "0111", + "0000000" when "1000", + "0010000" when "1001", + "0001000" when "1010", + "0000011" when "1011", + "1000110" when "1100", + "0100001" when "1101", + "0000110" when "1110", + "0001110" when "1111", + "1111111" when others; + +end behavioral; diff --git a/libraries/nexys2/seven_seg_mux.vhd b/libraries/nexys2/seven_seg_mux.vhd new file mode 100644 index 0000000..cddb127 --- /dev/null +++ b/libraries/nexys2/seven_seg_mux.vhd @@ -0,0 +1,57 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity seven_seg_mux is + port ( + clk_in: in std_logic; + + seg_0_in: in std_logic_vector(6 downto 0); + seg_1_in: in std_logic_vector(6 downto 0); + seg_2_in: in std_logic_vector(6 downto 0); + seg_3_in: in std_logic_vector(6 downto 0); + dps_in: in std_logic_vector(3 downto 0); + + seg_out: out std_logic_vector(6 downto 0); + dp_out: out std_logic; + an_out: out std_logic_vector(3 downto 0) + ); +end seven_seg_mux; + + +architecture Behavioral of seven_seg_mux is + signal an_reg: unsigned(1 downto 0); +begin + + -- Anode selection + + process(clk_in) + begin + if rising_edge(clk_in) then + an_reg <= an_reg + 1; + end if; + end process; + + with an_reg select an_out <= + "1110" when "00", + "1101" when "01", + "1011" when "10", + "0111" when others; + + + -- Select by anode + + with an_reg select seg_out <= + seg_0_in when "00", + seg_1_in when "01", + seg_2_in when "10", + seg_3_in when others; + + with an_reg select dp_out <= + dps_in(0) when "00", + dps_in(1) when "01", + dps_in(2) when "10", + dps_in(3) when others; + +end Behavioral; diff --git a/libraries/nexys2/tests/program.sh b/libraries/nexys2/tests/program.sh new file mode 100755 index 0000000..6d2fed4 --- /dev/null +++ b/libraries/nexys2/tests/program.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +djtgcfg -d Nexys2 -i 0 -f test_nexys2_mem_wb8_0.bit prog + diff --git a/libraries/nexys2/tests/test_nexys2_mem_wb8_0.vhd b/libraries/nexys2/tests/test_nexys2_mem_wb8_0.vhd new file mode 100644 index 0000000..d86880d --- /dev/null +++ b/libraries/nexys2/tests/test_nexys2_mem_wb8_0.vhd @@ -0,0 +1,220 @@ +library ieee; +use ieee.std_logic_1164.all; + +library utility; + +library work; + + +entity test_nexys2_mem_wb8_0 is + port ( + clk_50: in std_logic; + + DB: inout std_logic_vector(7 downto 0); + EppWRITE: in std_logic; + EppASTB: in std_logic; + EppDSTB: in std_logic; + EppWAIT: out std_logic; + + MemOE: out std_logic; + MemWR: out std_logic; + RamAdv: out std_logic; + RamCS: out std_logic; + RamClk: out std_logic; + RamCRE: out std_logic; + RamLB: out std_logic; + RamUB: out std_logic; + RamWait: in std_logic; + FlashRp: out std_logic; + FlashCS: out std_logic; + FlashStSts: in std_logic; + MemAdr: out std_logic_vector(23 downto 1); + MemDB: inout std_logic_vector(15 downto 0); + + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + Led: out std_logic_vector(7 downto 0); + sw: in std_logic_vector(7 downto 0); + btn: in std_logic_vector(3 downto 0) + ); +end test_nexys2_mem_wb8_0; + + +architecture behavioral of test_nexys2_mem_wb8_0 is + + signal clk: std_logic; + signal rst: std_logic; + signal cyc: std_logic; + signal mem_cyc: std_logic; + signal host_cyc: std_logic; + signal stb: std_logic; + signal we: std_logic; + signal ack: std_logic; + signal mem_ack: std_logic; + signal host_ack: std_logic; + signal adr: std_logic_vector(7 downto 0); + signal mem_adr: std_logic_vector(24 downto 0); + signal dat_mosi: std_logic_vector(7 downto 0); + signal dat_miso: std_logic_vector(7 downto 0); + signal mem_miso: std_logic_vector(7 downto 0); + signal host_miso: std_logic_vector(7 downto 0); + + signal d_MemOE: std_logic; + signal d_MemWR: std_logic; + signal d_RamAdv: std_logic; + signal d_RamCS: std_logic; + signal d_RamClk: std_logic; + signal d_RamCRE: std_logic; + signal d_RamUB: std_logic; + signal d_RamLB: std_logic; + signal d_RamWait: std_logic; + signal d_FlashRp: std_logic; + signal d_FlashCS: std_logic; + signal d_FlashStSts: std_logic; + signal d_MemAdr: std_logic_vector(23 downto 1); + signal d_MemDB_i: std_logic_vector(15 downto 0); + signal d_MemDB_o: std_logic_vector(15 downto 0); + + signal temp: std_logic_vector(7 downto 0); + +begin + + mem_adr <= adr(6) & "000000000000000000" & adr(5 downto 0); + e_mem: entity work.mem_wb8_0 + port map ( + rst_i => rst, + clk_i => clk, + + cyc_i => mem_cyc, + stb_i => stb, + we_i => we, + ack_o => mem_ack, + adr_i => mem_adr, + dat_i => dat_mosi, + dat_o => mem_miso, + + wait_cycles => temp(4 downto 0), + + MemOE => d_MemOE, + MemWR => d_MemWR, + RamAdv => d_RamAdv, + RamCS => d_RamCS, + RamClk => d_RamClk, + RamCRE => d_RamCRE, + RamUB => d_RamUB, + RamLB => d_RamLB, + RamWait => d_RamWait, + FlashRp => d_FlashRp, + FlashCS => d_FlashCS, + FlashStSts => d_FlashStSts, + MemAdr => d_MemAdr, + MemDB_i => d_MemDB_i, + MemDB_o => d_MemDB_o + ); + + e_wb_mux: entity utility.wb_mux2 + generic map ( + WIDTH => 8 + ) + port map ( + sel => adr(7), + + cyc_i => cyc, + ack_o => ack, + dat_o => dat_miso, + + cyc_o_0 => mem_cyc, + ack_i_0 => mem_ack, + dat_i_0 => mem_miso, + + cyc_o_1 => host_cyc, + ack_i_1 => host_ack, + dat_i_1 => host_miso + ); + + e_wb_dbg: entity work.wishbone_debugger + port map ( + clk_o => clk, + rst_o => rst, + + cyc_o => cyc, + stb_o => stb, + we_o => we, + ack_i => ack, + adr_o => adr, + dat_o => dat_mosi, + dat_i => dat_miso, + + clk_50 => clk_50, + seg => seg, + dp => dp, + an => an, + sw => sw, + btn => btn + ); + + e_host_ctrl: entity work.host_ctrl + port map ( + clk_i => clk, + rst_i => rst, + + d_rst_o => open, + d_flags_o => open, + debug_i => (others => '0'), + debug_o => open, + + d_cyc_i => host_cyc, + d_stb_i => stb, + d_we_i => we, + d_ack_o => host_ack, + d_adr_i => adr(2 downto 0), + d_dat_i => dat_mosi, + d_dat_o => host_miso, + + d_MemOE => d_MemOE, + d_MemWR => d_MemWR, + d_RamAdv => d_RamAdv, + d_RamCS => d_RamCS, + d_RamClk => d_RamClk, + d_RamCRE => d_RamCRE, + d_RamLB => d_RamLB, + d_RamUB => d_RamUB, + d_RamWait => d_RamWait, + d_FlashRp => d_FlashRp, + d_FlashCS => d_FlashCS, + d_FlashStSts => d_FlashStSts, + d_MemAdr => d_MemAdr, + d_MemDB_o => d_MemDB_o, + d_MemDB_i => d_MemDB_i, + + EppAstb => EppASTB, + EppDstb => EppDSTB, + EppWr => EppWRITE, + EppDB => DB, + EppWait => EppWAIT, + + MemOE => MemOE, + MemWR => MemWR, + RamAdv => RamAdv, + RamCS => RamCS, + RamClk => RamClk, + RamCRE => RamCRE, + RamLB => RamLB, + RamUB => RamUB, + RamWait => RamWait, + FlashRp => FlashRp, + FlashCs => FlashCs, + FlashStSts => FlashStSts, + MemAdr => MemAdr, + MemDB => MemDB, + + seg => open, + dp => open, + an => open, + Led => temp, + sw => sw + ); + Led <= temp; + +end behavioral; diff --git a/libraries/nexys2/tests/test_sim_mem_wb8_0.vhd b/libraries/nexys2/tests/test_sim_mem_wb8_0.vhd new file mode 100644 index 0000000..64e2956 --- /dev/null +++ b/libraries/nexys2/tests/test_sim_mem_wb8_0.vhd @@ -0,0 +1,158 @@ +library ieee; +use ieee.std_logic_1164.all; + +library simulated; + +library work; + + +entity test_sim_mem_wb8_0 is +end test_sim_mem_wb8_0; + + +architecture behavior of test_sim_mem_wb8_0 is + + constant clk_i_period: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal cyc_i: std_logic; + signal stb_i: std_logic; + signal we_i: std_logic; + signal ack_o: std_logic; + signal adr_i: std_logic_vector(24 downto 0); + signal dat_i: std_logic_vector(7 downto 0); + signal dat_o: std_logic_vector(7 downto 0); + + signal wait_cycles: std_logic_vector(4 downto 0); + + signal MemOE: std_logic; + signal MemWR: std_logic; + signal RamAdv: std_logic; + signal RamCS: std_logic; + signal RamClk: std_logic; + signal RamCRE: std_logic; + signal RamUB: std_logic; + signal RamLB: std_logic; + signal RamWait: std_logic; + signal FlashRp: std_logic; + signal FlashCS: std_logic; + signal FlashStSts: std_logic; + signal MemAdr: std_logic_vector(23 downto 1); + signal MemDB_i: std_logic_vector(15 downto 0); + signal MemDB_o: std_logic_vector(15 downto 0); + signal MemDB: std_logic_vector(15 downto 0); + + type test_name_t is ( + NONE, + RD_FLASH + ); + + signal test: test_name_t; + +begin + + p_test: process + begin + -- Initial values + test <= NONE; + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + adr_i <= (others => '0'); + dat_i <= (others => '0'); + wait_cycles <= "00110"; + + -- Reset + rst_i <= '1'; + wait for clk_i_period * 2; + rst_i <= '0'; + wait for 300 ns; -- T_PHEL for flash + + -- Read from flash + test <= RD_FLASH; + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "0000000000000000000000000"; + wait until ack_o = '1'; + cyc_i <= '0'; + stb_i <= '0'; + + wait; + end process; + + uut: entity work.mem_wb8_0 + port map ( + rst_i => rst_i, + clk_i => clk_i, + + cyc_i => cyc_i, + stb_i => stb_i, + we_i => we_i, + ack_o => ack_o, + adr_i => adr_i, + dat_i => dat_i, + dat_o => dat_o, + + wait_cycles => wait_cycles, + + MemOE => MemOE, + MemWR => MemWR, + RamAdv => RamAdv, + RamCS => RamCS, + RamClk => RamClk, + RamCRE => RamCRE, + RamUB => RamUB, + RamLB => RamLB, + RamWait => RamWait, + FlashRp => FlashRp, + FlashCS => FlashCS, + FlashStSts => FlashStSts, + MemAdr => MemAdr, + MemDB_i => MemDB_i, + MemDB_o => MemDB_o + ); + + MemDB <= (others => 'Z') when MemWR = '1' else MemDB_o; + + e_ram: entity simulated.mt45w8mw16bgx + port map ( + a => MemAdr, + dq => MemDB, + clk => RamClk, + adv_n => RamAdv, + cre => RamCRE, + ce_n => RamCS, + oe_n => MemOE, + we_n => MemWR, + lb_n => RamLB, + ub_n => RamUB, + rwait => RamWait + ); + + e_flash: entity simulated.js28f128j3d75 + port map ( + a(23 downto 1) => MemAdr, + a(0) => '0', + d => MemDB, + ce(2 downto 1) => "00", + ce(0) => FlashCS, + rp_n => FlashRp, + oe_n => MemOE, + we_n => MemWR, + sts => FlashStSts, + byte_n => '1', + vpen => '1' + ); + + clk_i_process: process + begin + clk_i <= '0'; + wait for clk_i_period/2; + clk_i <= '1'; + wait for clk_i_period/2; + end process; + +end; diff --git a/libraries/nexys2/wishbone_debugger.vhd b/libraries/nexys2/wishbone_debugger.vhd new file mode 100644 index 0000000..baa5751 --- /dev/null +++ b/libraries/nexys2/wishbone_debugger.vhd @@ -0,0 +1,253 @@ +-------------------------------------------------------------------------------- +-- Nexys2 Manual Wishbone Bus Master +-- +-- The purpose is to provide a ready-made harness to manually test and debug +-- Wishbone bus devices by hand on a Nexys2 board +-- +-- The intent is to use the Wishbone interface to test the functionality of a +-- device, not to test the Wishbone interface itself (The device's interface is +-- assumed to be functional) +-- +-- +-- address data +-- /-------\ /-------\ +-- -- -- -- -- +-- | | | | | | | | Seven-segment display +-- -- -- -- -- +-- | | | | | | | | +-- -- -- o -- -- +-- +-- /\ /\ /\ /\ Pushbuttons +-- \/ \/ \/ \/ +-- addr read write reset +-- +-- +-- Switches are used to enter an 8-bit value +-- +-- Address button latches value entered on the switchs into an internal address +-- holding register, which is displayed in hex on the left two 7seg digits +-- +-- Read button performs a Wishbone bus read cycle, latching the value into an +-- internal data holding register, which is displayed in hex on the right two +-- 7seg digits +-- +-- Write button performs a Wishbone bus write cycle, writing the value entered +-- on the switches and also displaying the written value in hex on the right two +-- 7seg digits +-- +-- The LEDs on the Nexys2 board are left available for flags put out by the DUT +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library utility; + + +entity wishbone_debugger is + port ( + -- Wishbone SYSCON + clk_o: out std_logic; + rst_o: out std_logic; + + -- wishbone bus master, 8-bit address, 8-bit data + cyc_o: out std_logic; + stb_o: out std_logic; + we_o: out std_logic; + ack_i: in std_logic; + adr_o: out std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + dat_i: in std_logic_vector(7 downto 0); + + -- Nexys2 IO pins + clk_50: in std_logic; + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + sw: in std_logic_vector(7 downto 0); + btn: in std_logic_vector(3 downto 0) + ); +end wishbone_debugger; + + +architecture behavioral of wishbone_debugger is + + type state_t is (S_IDLE, S_READ, S_WRITE); + + signal rst: std_logic; + signal clk: std_logic; + + signal rd_stb: std_logic; + signal wr_stb: std_logic; + signal adr_stb: std_logic; + signal adr_reg: std_logic_vector(7 downto 0); + signal dat_r_stb: std_logic; + signal dat_w_stb: std_logic; + signal dat_reg: std_logic_vector(7 downto 0); + + signal state_reg: state_t; + signal state_next: state_t; + + -- Signal conditioning + signal clk_div_reg: unsigned(15 downto 0); + signal seg_3: std_logic_vector(6 downto 0); + signal seg_2: std_logic_vector(6 downto 0); + signal seg_1: std_logic_vector(6 downto 0); + signal seg_0: std_logic_vector(6 downto 0); + +begin + + -- Wishbone controller + + process (rst, clk, state_next) + begin + if rising_edge(clk) then + if rst = '1' then + state_reg <= S_IDLE; + else + state_reg <= state_next; + end if; + end if; + end process; + + process (state_reg, rd_stb, wr_stb, ack_i) + begin + state_next <= state_reg; + cyc_o <= '0'; + stb_o <= '0'; + we_o <= '0'; + dat_r_stb <= '0'; + dat_w_stb <= '0'; + + case state_reg is + when S_IDLE => + if rd_stb = '1' then + state_next <= S_READ; + elsif wr_stb = '1' then + state_next <= S_WRITE; + end if; + + when S_READ => + cyc_o <= '1'; + stb_o <= '1'; + if ack_i = '1' then + dat_r_stb <= '1'; + state_next <= S_IDLE; + end if; + + when S_WRITE => + cyc_o <= '1'; + stb_o <= '1'; + we_o <= '1'; + if ack_i = '1' then + dat_w_stb <= '1'; + state_next <= S_IDLE; + end if; + + when others => + state_next <= S_IDLE; + end case; + end process; + + process (rst, clk, adr_stb, sw) + begin + if rising_edge(clk) then + if rst = '1' then + adr_reg <= (others => '0'); + elsif adr_stb = '1' then + adr_reg <= sw; + end if; + end if; + end process; + + process (rst, clk, dat_r_stb, dat_w_stb, dat_i, sw) + begin + if rising_edge(clk) then + if rst = '1' then + dat_reg <= (others => '0'); + elsif dat_r_stb = '1' then + dat_reg <= dat_i; + elsif dat_w_stb = '1' then + dat_reg <= sw; + end if; + end if; + end process; + + rst_o <= rst; + clk_o <= clk; + adr_o <= adr_reg; + dat_o <= sw; + + + -- Signal conditioning + + process (clk_50) + begin + if rising_edge(clk_50) then + clk_div_reg <= clk_div_reg + 1; + end if; + end process; + + clk <= clk_50; + rst <= btn(0); -- No debouncing necessary + adr_stb <= btn(3); -- No debouncing necessary + + e_debounce_rd: entity utility.debounce + port map ( + clk => clk, + clk_samp => clk_div_reg(15), + btn_deb => open, + btn_rise => rd_stb, + btn_fall => open, + btn => btn(2) + ); + + e_debounce_wr: entity utility.debounce + port map ( + clk => clk, + clk_samp => clk_div_reg(15), + btn_deb => open, + btn_rise => wr_stb, + btn_fall => open, + btn => btn(1) + ); + + e_hex3: entity work.seven_seg_hex + port map ( + data_in => adr_reg(7 downto 4), + seg_out => seg_3 + ); + + e_hex2: entity work.seven_seg_hex + port map ( + data_in => adr_reg(3 downto 0), + seg_out => seg_2 + ); + + e_hex1: entity work.seven_seg_hex + port map ( + data_in => dat_reg(7 downto 4), + seg_out => seg_1 + ); + + e_hex0: entity work.seven_seg_hex + port map ( + data_in => dat_reg(3 downto 0), + seg_out => seg_0 + ); + + e_7seg_mux: entity work.seven_seg_mux + port map ( + clk_in => clk_div_reg(15), + seg_0_in => seg_0, + seg_1_in => seg_1, + seg_2_in => seg_2, + seg_3_in => seg_3, + dps_in => "1011", + seg_out => seg, + dp_out => dp, + an_out => an + ); + +end behavioral; diff --git a/libraries/ps2/ps2_host.vhd b/libraries/ps2/ps2_host.vhd new file mode 100644 index 0000000..00ea8b1 --- /dev/null +++ b/libraries/ps2/ps2_host.vhd @@ -0,0 +1,482 @@ +-------------------------------------------------------------------------------- +-- Notes: +-- +-- The interface to the system is Wishbone-like - ACK and DAT signals are only +-- considered valid when the corresponding STB signal is asserted, and both may +-- be asserted or contain data even when STB is not asserted, which must be +-- ignored. This applies to both tx_* and rx_* interfaces. +-- +-- If any transfer is in progress when the system signals that it wants to +-- transmit a byte via tx_stb_i, the controller will stall the system by not +-- asserting tx_ack_o until both the current transfer is finished and, if the +-- transfer is from device to the host, the system acknowledges the received +-- byte by asserting rx_ack_i. +-- +-- The controller will not accept new bytes to transmit to the device when +-- inhibit is asserted, and will stall the system by not asserting tx_ack_o. +-- If a host to device transfer is already in progress when inhibit is +-- asserted, the transfer is completed before inhibiting the device. +-- +-- If a device to host transfer is in progress when inhibit is asserted, the +-- transfer will be cancelled and received bits discarded unless the transfer +-- has reached the 11th (stop) bit. If the transfer of the stop bit is in +-- progress, the controller will finish receiving the byte and then inhibit the +-- device. The received byte will be signalled with rx_stb_o and must be +-- acknowledged by the system with rx_ack_i. This behavior is required by the +-- PS2 serial protocol spec. +-- +-- If auto_inhibit is asserted, the controller will inhibit the device from +-- transmitting after each byte received from the device, and will uninhibit the +-- device once the received byte is acknowledged by the system via rx_ack_i. +-- Use of auto_inhibit is recommended unless it is known that a device will not +-- behave predictably when inhibited. +-- +-- Some of the error signals have imprecise timings, which is to say that it +-- cannot strictly be determined when the error signal should be checked or +-- possibly which transfer caused the error. If transfer errors occur, you have +-- bigger problems that won't be solved by knowing which transfer caused the +-- error, so this is fine. The variety in types of distinguishable errors is +-- certainly overkill, as the response to any errors is generally: +-- 1. Discard data for receive errors, retry for transmit errors +-- 2. Reset the controller interface if (1) doesn't work +-- 3. Give up completely if neither (1) nor (2) work +-- +-- Error signals: +-- tx_err_nack - Asserted when the device fails to acknowledge a byte +-- transmitted from the host +-- Always valid, but will indicate a detected error only upon +-- transfer completion of each host-transmitted byte - without +-- "done" signalling, the timing relative to the beginning of a +-- transfer is imprecise +-- Deasserted when the interface is reset +-- tx_err_timeout - Asserted when the device fails to transition the serial CLK +-- line within some time limit during host to device transfer +-- Always valid, but timing of error detection (and the error +-- itself) relative to the beginning of a transfer is imprecise +-- Deasserted when the interface is reset +-- rx_err_timeout - Asserted when the device fails to transition the serial CLK +-- line within some time limit during device to host transfer +-- Always valid, but timing of error detection (and the error +-- itself) relative to the beginning of a transfer is imprecise, +-- and rx_stb_o will not be asserted after a receive timeout as +-- no complete transfer took place +-- Deasserted when the interface is reset +-- rx_err_missed - Asserted when the device begins another transfer to the host +-- before the system has acknowledged a previous transfer +-- Always valid, but timing of error detection (and the error +-- itself) is imprecise +-- Deasserted when the previously received byte is acknowledged +-- by the host via rx_ack_i +-- rx_err_parity - Asserted when a parity error is detected for a received byte +-- Valid only when rx_stb_o is asserted (may spuriously assert +-- while rx_stb_o is deasserted) +-- Recomputed with each received byte +-- rx_err_start - Asserted when a device to host transfer sends a one-valued +-- start bit +-- Valid only when rx_stb_o is asserted (may spuriously assert +-- while rx_stb_o is deasserted) +-- Recomputed with each received byte +-- rx_err_stop - Asserted when a device to host transfer sends a zero-valued +-- stop bit +-- Valid only when rx_stb_o is asserted (may spuriously assert +-- while rx_stb_o is deasserted) +-- Recomputed with each received byte +-- +-- TODO: Accept watchdog timeout tick count as input signal from system +-- TODO: Use separate (not watchdog timeout) tick count for transmit inhibit +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.std_logic_misc.all; + + +entity ps2_host is + port ( + -- Wishbone SYSCON + rst_i: in std_logic; + clk_i: in std_logic; + + -- Transmit + tx_stb_i: in std_logic; -- Trigger transmission of a byte + tx_ack_o: out std_logic; -- Ready to transmit + tx_dat_i: in std_logic_vector(7 downto 0); + + -- Receive + rx_stb_o: out std_logic; -- Byte from device is ready + rx_ack_i: in std_logic; -- Acknowledge a received byte + rx_dat_o: out std_logic_vector(7 downto 0); + + -- Configuration + inhibit: in std_logic; -- Cancel and prevent transmission from device + auto_inhibit: in std_logic; -- Prevent transmission from device until byte is read + + -- Error signals + tx_err_nack: out std_logic; -- Transmission not acknowledged + tx_err_timeout: out std_logic; -- Device took too long to transition clock during tx + rx_err_timeout: out std_logic; -- Device took too long to transition clock during rx + rx_err_missed: out std_logic; -- Byte from device was missed + rx_err_parity: out std_logic; -- Parity error detected + rx_err_start: out std_logic; -- Start bit was not 1 + rx_err_stop: out std_logic; -- Stop bit was not 1 + + -- Serial bus + ps2_clk: inout std_logic; + ps2_data: inout std_logic + ); +end ps2_host; + + +architecture behavioral of ps2_host is + + type state_t is ( + S_READY, S_INHIBIT, + S_RX_SHIFT, S_RX_HOLD, + S_TX_START, S_TX_SHIFT + ); + + signal state_reg: state_t; + signal next_state: state_t; + + -- Shifter and control + -- start bit, 8 data bits, parity bit, stop bit all included in shift register + signal count_reset: std_logic; + signal data_count_reg: unsigned(3 downto 0); + signal count_done: std_logic; + signal tx_latch_en: std_logic; + signal transmit: std_logic; + signal shift_en: std_logic; + signal data_in_parity: std_logic; + signal data_shift_reg: std_logic_vector(10 downto 0); + + -- Watchdog + constant TICKS_200us: unsigned(15 downto 0) := x"2710"; -- Assumes 50MHz clock + signal watchdog_kick: std_logic; + signal watchdog_reg: unsigned(15 downto 0); + signal watchdog_expire: std_logic; + + -- Error tracking registers + signal tx_nack_update: std_logic; + signal tx_nack_reg: std_logic; + + signal tx_timeout_set: std_logic; + signal tx_timeout_reg: std_logic; + + signal rx_parity_reg: std_logic; + + signal rx_timeout_set: std_logic; + signal rx_timeout_reg: std_logic; + + signal rx_missed_set: std_logic; + signal rx_missed_rst: std_logic; + signal rx_missed_reg: std_logic; + + -- Serial bus signal conditioning + signal host_clk: std_logic; + signal host_data: std_logic; + signal ps2_clk_int: std_logic; + signal ps2_data_int: std_logic; + signal edge_detector_reg: std_logic_vector(1 downto 0); + signal ps2_rising_edge: std_logic; + signal ps2_falling_edge: std_logic; + +begin + + ---------------------------------------------------------------------------- + -- State machine + + process (rst_i, clk_i, next_state) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + state_reg <= S_READY; + else + state_reg <= next_state; + end if; + end if; + end process; + + process (state_reg, ps2_rising_edge, ps2_falling_edge, watchdog_expire, tx_stb_i, rx_ack_i, inhibit, auto_inhibit, count_done, data_shift_reg) + begin + -- Default signals + next_state <= state_reg; -- Remain in current state + rx_stb_o <= '0'; -- Don't present a received byte + tx_ack_o <= '0'; -- Don't accept a transmit byte + tx_latch_en <= '0'; -- Don't latch data from system to transmit + shift_en <= '0'; -- Don't shift data through the serial bus or count bits + watchdog_kick <= '0'; -- Allow watchdog to run + -- TODO: count_reset should default to 1 and be explicitly disabled when receiving + count_reset <= '0'; -- Leave the bit counter alone + host_clk <= '1'; -- Allow device to control serial clock line + host_data <= '1'; -- Allow device to control serial data line + tx_timeout_set <= '0'; -- Don't flag TX timeout + rx_timeout_set <= '0'; -- Don't flag RX timeout + rx_missed_rst <= '0'; -- Don't reset the RX missed flag + transmit <= '0'; + + case state_reg is + when S_READY => + -- Accept either new Tx or Rx byte + tx_ack_o <= '1'; -- Accept byte to transmit + watchdog_kick <= '1'; -- Stop watchdog from running and reset + count_reset <= '1'; -- Reset bit counter + if inhibit = '1' then + -- If inhibit requested, don't accept byte to transmit + tx_ack_o <= '0'; + next_state <= S_INHIBIT; + elsif tx_stb_i = '1' then + -- Grab a byte from the system and start transmission + tx_latch_en <= '1'; + next_state <= S_TX_START; + elsif ps2_falling_edge = '1' then + -- Begin receiving from device + count_reset <= '0'; -- Allow the first falling edge to be counted + shift_en <= '1'; + next_state <= S_RX_SHIFT; + end if; + + when S_INHIBIT => + -- Don't accept any new Tx or Rx bytes until inhibit is lowered + host_clk <= '0'; + if inhibit = '0' then + next_state <= S_READY; + end if; + + when S_RX_SHIFT => + if inhibit = '1' and count_done = '0' then + -- Don't allow inhibit during last (stop) bit, otherwise + -- cancel and discard reception + next_state <= S_INHIBIT; + elsif ps2_falling_edge = '1' then + -- Sample data on falling edge and kick the watchdog + watchdog_kick <= '1'; + shift_en <= '1'; + --elsif ps2_rising_edge = '1' and count_done = '1' then + elsif count_done = '1' then + -- Done on XrisingXedgeX of last bit + -- Don't wait for the rising edge so we can keep the clock + -- low when auto-inhibiting - this prevents us from dropping + -- the clock due to auto-inhibit and picking up our own + -- falling edge as the beginning of a new byte, causing + -- spurrious rx timeout errors + next_state <= S_RX_HOLD; + elsif watchdog_expire = '1' then + -- If device stops responding, abort and return to ready + rx_timeout_set <= '1'; + next_state <= S_READY; + end if; + + when S_RX_HOLD => + -- Report reception to system and don't accept bytes to transmit + -- until the shift register is made available by acknowledgement + rx_stb_o <= '1'; + count_reset <= '1'; -- This prevents underflow when auto-inhibiting for a single clock cycle + if auto_inhibit = '1' or inhibit = '1' then + -- If inhibit was asserted during the stop bit, the byte is + -- received, and we inhibit now - or if auto-inhibition is + -- requested after reception to avoid missing data + host_clk <= '0'; + end if; + if rx_ack_i = '1' then + -- Upon acknowledgement, either return to ready or handle + -- inhibition that was deferred due to assertion during the + -- stop bit + rx_missed_rst <= '1'; + if inhibit = '1' then + next_state <= S_INHIBIT; + else + -- We may reenter the ready state while the device is + -- still holding the clock low, since we don't wait for + -- the rising edge to enter rx_hold (to support auto- + -- inhibit) + -- This will not cause problems, as transmitting a new + -- byte begins with inhibiting the deivce for a time + -- anyway, and the device knows what it's doing when it + -- wants to send the next byte + next_state <= S_READY; + end if; + end if; + + when S_TX_START => + -- Inhibit device and transmit the start bit + transmit <= '1'; + host_clk <= '0'; + host_data <= data_shift_reg(0); + if watchdog_expire = '1' then + -- Use the watchdog as a timer to inhibit the device for some time + watchdog_kick <= '1'; + next_state <= S_TX_SHIFT; + end if; + + when S_TX_SHIFT => + transmit <= '1'; + host_data <= data_shift_reg(0); -- Transmit data bit (will still be start bit until first falling edge) + if ps2_falling_edge = '1' then + -- Change data on falling edge and kick the watchdog + -- The shift register also shifts data back in, which will catch the device ACK + watchdog_kick <= '1'; + shift_en <= '1'; + if count_done = '1' then + tx_nack_update <= '1'; + end if; + elsif ps2_rising_edge = '1' and count_done = '1' then + -- Done on rising edge of last bit + next_state <= S_READY; + elsif watchdog_expire = '1' then + -- If device stops responding, abort and return to ready + tx_timeout_set <= '1'; + next_state <= S_READY; + end if; + + when others => + next_state <= S_READY; + end case; + end process; + + + ---------------------------------------------------------------------------- + -- Error tracking registers + + process (rst_i, clk_i, tx_nack_update, ps2_data_int) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + tx_nack_reg <= '0'; + elsif tx_nack_update = '1' then + tx_nack_reg <= ps2_data_int; + end if; + end if; + end process; + tx_err_nack <= tx_nack_reg; + + process (rst_i, clk_i, tx_timeout_set) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + tx_timeout_reg <= '0'; + elsif tx_timeout_set = '1' then + tx_timeout_reg <= '1'; + end if; + end if; + end process; + tx_err_timeout <= tx_timeout_reg; + + process (rst_i, clk_i, count_reset, shift_en, rx_parity_reg, ps2_data_int) + begin + if rising_edge(clk_i) then + if rst_i = '1' or count_reset = '1' then + rx_parity_reg <= '0'; + elsif shift_en = '1' then + rx_parity_reg <= rx_parity_reg xor ps2_data_int; + end if; + end if; + end process; + rx_err_parity <= rx_parity_reg; + rx_err_start <= data_shift_reg(0); + rx_dat_o <= data_shift_reg(8 downto 1); + rx_err_stop <= not data_shift_reg(10); + + process (rst_i, clk_i, rx_timeout_set) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + rx_timeout_reg <= '0'; + elsif rx_timeout_set = '1' then + rx_timeout_reg <= '1'; + end if; + end if; + end process; + rx_err_timeout <= rx_timeout_reg; + + process (rst_i, clk_i, rx_missed_set) + begin + if rising_edge(clk_i) then + if rst_i = '1' or rx_missed_rst = '1' then + rx_missed_reg <= '0'; + elsif rx_missed_set = '1' then + rx_missed_reg <= '1'; + end if; + end if; + end process; + rx_err_missed <= rx_missed_reg; + + + ---------------------------------------------------------------------------- + -- Data shift register and bit counter + + -- Count down to zero and halt + process (rst_i, clk_i, count_reset, shift_en, data_count_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' or count_reset = '1' then + data_count_reg <= x"b"; + elsif shift_en = '1' then + data_count_reg <= data_count_reg - 1; + end if; + end if; + end process; + count_done <= not or_reduce(std_logic_vector(data_count_reg)); + + -- Transmit data latched when tx_latch_en is active + -- LSBs shifted out and PS2 data shifted into the MSB when shift_en is active + -- Used for both Tx and Rx + process (clk_i, tx_latch_en, shift_en, data_in_parity, tx_dat_i, ps2_data_int, data_shift_reg, transmit) + begin + if rising_edge(clk_i) then + if tx_latch_en = '1' then + data_shift_reg <= '1' & data_in_parity & tx_dat_i & '0'; + elsif shift_en = '1' then + -- The "or transmit" signal forces ones to be shifted in while + -- transmitting, which prevents from sampling our own start bit + -- and shifting it out during the acknowledge period, which + -- would acknowledge our own transmission + data_shift_reg <= (ps2_data_int or transmit) & data_shift_reg(data_shift_reg'high downto 1); + end if; + end if; + end process; + data_in_parity <= not xor_reduce(tx_dat_i); + + + ---------------------------------------------------------------------------- + -- Watchdog timer, reset to 200us when kicked, stop at zero + + process (rst_i, clk_i, watchdog_kick, watchdog_expire, watchdog_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' or watchdog_kick = '1' then + watchdog_reg <= TICKS_200us; + elsif watchdog_expire = '0' then + watchdog_reg <= watchdog_reg - 1; + end if; + end if; + end process; + watchdog_expire <= not or_reduce(std_logic_vector(watchdog_reg)); + + + ---------------------------------------------------------------------------- + -- PS2 signal conditioning + + -- Strengthen input signals ('Z','H' -> '1') for simulation + ps2_clk_int <= '0' when ps2_clk = '0' else '1'; + ps2_data_int <= '0' when ps2_data = '0' else '1'; + + -- Detect rising and falling edges + process (rst_i, clk_i, edge_detector_reg, ps2_clk_int) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + edge_detector_reg <= (others => '1'); + else + edge_detector_reg <= edge_detector_reg(edge_detector_reg'high-1 downto 0) & ps2_clk_int; + end if; + end if; + end process; + ps2_falling_edge <= '1' when edge_detector_reg = "10" else '0'; + ps2_rising_edge <= '1' when edge_detector_reg = "01" else '0'; + + -- Weaken output signals to allow device to pull low + ps2_clk <= '0' when host_clk = '0' else 'Z'; + ps2_data <= '0' when host_data = '0' else 'Z'; + +end behavioral; diff --git a/libraries/ps2/ps2_host_wb.vhd b/libraries/ps2/ps2_host_wb.vhd new file mode 100644 index 0000000..dbd0eac --- /dev/null +++ b/libraries/ps2/ps2_host_wb.vhd @@ -0,0 +1,297 @@ +-------------------------------------------------------------------------------- +-- TODO: https://eecs.umich.edu/courses/doing_dsp/handout/SRL16E.pdf +-- Play with using shift-registers to make this more dense +-- +-- +---+---+---+---+---+---+---+---+ +-- | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +-- +---+---+---+---+---+---+---+---+ +-- 0 |INH|AIH| |ENA| CTRL +-- +---+---+---+---+---+---+---+---+ +-- 1 |OER|FER|PER| |TXE|TXR|RXF|RXR| IMASK +-- +---+---+---+---+---+---+---+---+ +-- 2 |OER|FER|PER| |TXE|TXR|RXF|RXR| IFLAG (W1C) +-- +---+---+---+---+---+---+---+---+ +-- 3 | |TTO|RTO|NCK| ERROR (RO) +-- +-------------------+---+---+---+ +-- 4 | DATA | DATA +-- +-------------------------------+ +-- 5 | (RESERVED) | +-- +-------------------------------+ +-- 6 | (RESERVED) | +-- +-------------------------------+ +-- 7 | (RESERVED) | +-- +-------------------------------+ +-- +-- CTRL register bits: +-- ENA - Enable interface, interface and FIFOs are cleared and reset when 0 +-- AIH - Auto-inhibit device from sending if FIFO is full +-- INH - Inhibit device from sending +-- +-- IMASK/IFLAG register bits: +-- RXR - Receiver has at least one byte ready +-- Deasserts when all DATA read +-- RXF - Receiver FIFO is full +-- Deasserts on read from DATA +-- TXR - Transmitter ready to accept at least one byte +-- Deasserts on when TX FIFO is full +-- TXE - Transmitter FIFO is empty +-- Deasserts on write to DATA +-- PER - Receiver parity error +-- Write one to clear +-- FER - Receiver framing error, either start or stop bit was incorrect +-- Write one to clear +-- OER - Receiver FIFO overrun, at least one byte was lost due to lack of space +-- Write one to clear +-- +-- ERROR register bits, cleared by writing ENA to zero: +-- TTO - Transmitter timeout error, PS2 clock stopped unexpectedly +-- RTO - Receiver timeout error, PS2 clock stopped unexpectedly +-- NER - Transmitter NACK error, at least one byte was not acknowledged +-- +-- Notes: +-- +-- It's a good idea to always use the auto-inhibit feature, as it allows the +-- device to be informed of and prevent FIFO spillage, possibly retransmitting +-- or performing more queueing on its end. +-- +-- User must check flags before reading/writing DATA register to ensure data is +-- available in RX or space is available in TX, otherwise undefined data will be +-- returned or written data will be lost. +-- +-- TODO: Add timeout/start bit timer divisors +-- TODO: Use generics for FIFO capacities +-- TODO: Use constants for register addresses to allow easy rearranging later +-- +-- FIXME: Missing OER signal (probably framing and parity errors too) +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +library utility; +library work; + + +entity ps2_host_wb is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(2 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + rx_ready: out std_logic; + rx_full: out std_logic; + tx_ready: out std_logic; + tx_empty: out std_logic; + + err_nack: out std_logic; + err_txto: out std_logic; + err_rxto: out std_logic; + err_missed: out std_logic; + err_parity: out std_logic; + err_framing: out std_logic; + + ps2_clk: inout std_logic; + ps2_data: inout std_logic + ); +end ps2_host_wb; + + +architecture behavioral of ps2_host_wb is + + -- Bus-exposed registers + signal ctrl_reg: std_logic_vector(7 downto 0); + signal imask_reg: std_logic_vector(7 downto 0); + signal iflag_reg: std_logic_vector(7 downto 0); + signal error_vec: std_logic_vector(7 downto 0); + + -- Control register bit signals + signal ctrl_ena: std_logic; + signal ctrl_inhibit: std_logic; + signal ctrl_auto_inh: std_logic; + + -- Interface connections + signal iface_rst: std_logic; + signal tx_err_nack: std_logic; + signal tx_err_timeout: std_logic; + signal rx_err_timeout: std_logic; + signal rx_err_missed: std_logic; + signal rx_err_parity: std_logic; + signal rx_err_start: std_logic; + signal rx_err_stop: std_logic; + + -- Transmit FIFO connections + signal txqt_stb: std_logic; + signal txqt_ack: std_logic; + signal txqt_dat: std_logic_vector(7 downto 0); + signal txqh_stb: std_logic; + -- txqh_ack left unconnected to prevent wb bus stalls when full + -- txqh_dat comes from dat_i + signal txq_empty: std_logic; + signal txq_full: std_logic; + + -- Receive FIFO connections + signal rxqh_stb: std_logic; + signal rxqh_ack: std_logic; + signal rxqh_dat: std_logic_vector(7 downto 0); + signal rxqt_stb: std_logic; + signal rxqt_ack: std_logic; + signal rxqt_dat: std_logic_vector(7 downto 0); + signal rxq_empty: std_logic; + signal rxq_full: std_logic; + + signal data_stb: std_logic; + +begin + + -- Wishbone interface + process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + ctrl_reg <= (others => '0'); + imask_reg <= (others => '0'); + iflag_reg <= (others => '0'); + else + -- Perform write cycle + if cyc_i = '1' and stb_i = '1' and we_i = '1' then + case adr_i is + when "000" => ctrl_reg <= dat_i; + when "001" => imask_reg <= dat_i; + when "010" => iflag_reg <= iflag_reg and (not dat_i); -- W1C + when "011" => null; -- RO + when "100" => null; -- Attached to FIFOs + when others => null; + end case; + end if; + + -- Latch interrupt flags + iflag_reg(0) <= rxqt_stb; -- using !rxq_empty will trigger interrupt before data makes it through the pipeline FIFO + iflag_reg(1) <= rxq_full; + iflag_reg(2) <= not txq_full; + iflag_reg(3) <= txq_empty; + + -- Catch receive error flags when the interface reports a new byte + if rxqh_stb = '1' then + iflag_reg(5) <= iflag_reg(5) or rx_err_parity; + iflag_reg(6) <= iflag_reg(6) or rx_err_start or rx_err_stop; + iflag_reg(7) <= iflag_reg(7) or rx_err_missed; + end if; + end if; + end if; + end process; + + error_vec <= "00000" & tx_err_timeout & rx_err_timeout & tx_err_nack; + + with adr_i select dat_o <= + ctrl_reg when "000", + imask_reg when "001", + iflag_reg when "010", + error_vec when "011", + rxqt_dat when "100", + (others => '1') when others; + + ack_o <= '1'; + + -- Interrupt output signals + rx_ready <= iflag_reg(0) and imask_reg(0); + rx_full <= iflag_reg(1) and imask_reg(1); + tx_ready <= iflag_reg(2) and imask_reg(2); + tx_empty <= iflag_reg(3) and imask_reg(3); + + err_missed <= iflag_reg(5) and imask_reg(5); + err_framing <= iflag_reg(6) and imask_reg(6); + err_parity <= iflag_reg(7) and imask_reg(7); + + err_nack <= tx_err_nack; + err_rxto <= rx_err_timeout; + err_txto <= tx_err_timeout; + + -- Control register bit signals + ctrl_inhibit <= ctrl_reg(7); + ctrl_auto_inh <= ctrl_reg(6); + ctrl_ena <= ctrl_reg(0); + + data_stb <= (cyc_i and stb_i) when adr_i = "100" else '0'; + txqh_stb <= data_stb when we_i = '1' else '0'; + rxqt_ack <= data_stb when we_i = '0' else '0'; + + iface_rst <= rst_i or (not ctrl_ena); + + e_txq: entity utility.fifo + generic map ( + WIDTH => 8, + COUNT => 4 + ) + port map ( + rst_i => iface_rst, + clk_i => clk_i, + + h_stb_i => txqh_stb, + h_ack_o => open, + h_dat_i => dat_i, + + t_stb_o => txqt_stb, + t_ack_i => txqt_ack, + t_dat_o => txqt_dat, + + is_empty => txq_empty, + is_full => txq_full + ); + + e_rxq: entity utility.fifo + generic map ( + WIDTH => 8, + COUNT => 4 + ) + port map ( + rst_i => iface_rst, + clk_i => clk_i, + + h_stb_i => rxqh_stb, + h_ack_o => rxqh_ack, + h_dat_i => rxqh_dat, + + t_stb_o => rxqt_stb, + t_ack_i => rxqt_ack, + t_dat_o => rxqt_dat, + + is_empty => rxq_empty, + is_full => rxq_full + ); + + e_ps2: entity work.ps2_host + port map ( + rst_i => iface_rst, + clk_i => clk_i, + + tx_stb_i => txqt_stb, + tx_ack_o => txqt_ack, + tx_dat_i => txqt_dat, + + rx_stb_o => rxqh_stb, + rx_ack_i => rxqh_ack, + rx_dat_o => rxqh_dat, + + inhibit => ctrl_inhibit, + auto_inhibit => ctrl_auto_inh, + + tx_err_nack => tx_err_nack, + tx_err_timeout => tx_err_timeout, + rx_err_timeout => rx_err_timeout, + rx_err_missed => rx_err_missed, + rx_err_parity => rx_err_parity, + rx_err_start => rx_err_start, + rx_err_stop => rx_err_stop, + + ps2_clk => ps2_clk, + ps2_data => ps2_data + ); + +end behavioral; diff --git a/libraries/ps2/tests/nexys2.vhd b/libraries/ps2/tests/nexys2.vhd new file mode 100644 index 0000000..da3a957 --- /dev/null +++ b/libraries/ps2/tests/nexys2.vhd @@ -0,0 +1,98 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + +library nexys2_lib; +library work; + + +entity nexys2 is + port ( + clk_50: in std_logic; + + -- 7-segment display + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + + -- Misc + Led: out std_logic_vector(7 downto 0); + sw: in std_logic_vector(7 downto 0); + btn: in std_logic_vector(3 downto 0); + + -- PS2 serial + PS2C: inout std_logic; + PS2D: inout std_logic + ); +end nexys2; + + +architecture behavioral of nexys2 is + + -- Wishbone SYSCON + signal clk: std_logic; + signal rst: std_logic; + + -- Wishbone bus + signal cyc: std_logic; + signal stb: std_logic; + signal we: std_logic; + signal ack: std_logic; + signal adr: std_logic_vector(7 downto 0); + signal dat_mosi: std_logic_vector(7 downto 0); + signal dat_miso: std_logic_vector(7 downto 0); + +begin + + e_ps2_host: entity work.ps2_host_wb + port map ( + clk_i => clk, + rst_i => rst, + + cyc_i => cyc, + stb_i => stb, + we_i => we, + ack_o => ack, + adr_i => adr(2 downto 0), + dat_i => dat_mosi, + dat_o => dat_miso, + + rx_ready => Led(0), + rx_full => Led(1), + tx_ready => Led(2), + tx_empty => Led(3), + + err_nack => open, + err_txto => open, + err_rxto => Led(7), + err_missed => Led(6), + err_parity => Led(5), + err_framing => Led(4), + + ps2_clk => PS2C, + ps2_data => PS2D + ); + + e_manual_wb: entity nexys2_lib.wishbone_debugger + port map ( + clk_o => clk, + rst_o => rst, + + cyc_o => cyc, + stb_o => stb, + we_o => we, + ack_i => ack, + adr_o => adr, + dat_o => dat_mosi, + dat_i => dat_miso, + + clk_50 => clk_50, + seg => seg, + dp => dp, + an => an, + sw => sw, + btn => btn + ); + +end behavioral; diff --git a/libraries/ps2/tests/program.sh b/libraries/ps2/tests/program.sh new file mode 100755 index 0000000..14d6689 --- /dev/null +++ b/libraries/ps2/tests/program.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog +#djtgcfg -d Nexys2 -i 0 -f OnBoardMemCfg.bit prog + diff --git a/libraries/ps2/tests/test_ps2_host.vhd b/libraries/ps2/tests/test_ps2_host.vhd new file mode 100644 index 0000000..3ed07e0 --- /dev/null +++ b/libraries/ps2/tests/test_ps2_host.vhd @@ -0,0 +1,220 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; +library simulated; + + +entity test_ps2_host is +end test_ps2_host; + + +architecture behavior of test_ps2_host is + + constant clk_i_period: time := 10 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal tx_stb_i: std_logic; + signal tx_ack_o: std_logic; + signal tx_dat_i: std_logic_vector(7 downto 0); + + signal rx_stb_o: std_logic; + signal rx_ack_i: std_logic; + signal rx_dat_o: std_logic_vector(7 downto 0); + + signal inhibit: std_logic; + signal auto_inhibit: std_logic; + + signal tx_err_nack: std_logic; + signal tx_err_timeout: std_logic; + signal rx_err_timeout: std_logic; + signal rx_err_missed: std_logic; + signal rx_err_parity: std_logic; + signal rx_err_start: std_logic; + signal rx_err_stop: std_logic; + + signal tx_byte: std_logic_vector(7 downto 0); + + signal ps2_clk: std_logic; + signal ps2_data: std_logic; + +begin + + test: process + begin + -- Initial values + tx_stb_i <= '0'; + tx_dat_i <= x"00"; + rx_ack_i <= '0'; + inhibit <= '0'; + auto_inhibit <= '0'; + + -- Reset + rst_i <= '1'; + wait for clk_i_period; + rst_i <= '0'; + wait for clk_i_period; + + wait for 200 us; + + -- Transmit to device normally + tx_stb_i <= '1'; + tx_dat_i <= x"a5"; + wait until rising_edge(clk_i); + while tx_ack_o = '0' loop + wait until rising_edge(clk_i); + end loop; + tx_stb_i <= '0'; + wait for 1 ms; + assert tx_err_nack = '0' report "NACK" severity error; + assert tx_err_timeout = '0' report "TX timeout" severity error; + + -- Receive from device normally + tx_byte <= x"c3"; + wait until rx_stb_o = '1'; + assert rx_err_timeout = '0' report "RX timeout" severity error; + assert rx_err_missed = '0' report "RX missed" severity error; + assert rx_err_parity = '0' report "RX parity" severity error; + assert rx_err_start = '0' report "RX start" severity error; + assert rx_err_stop = '0' report "RX stop" severity error; + rx_ack_i <= '1'; + wait until rising_edge(clk_i); + rx_ack_i <= '0'; + + wait for 200 us; + + -- Attempt to transmit while transmitting (waits until finished) + wait until falling_edge(clk_i); + tx_stb_i <= '1'; + tx_dat_i <= x"12"; + wait until rising_edge(clk_i); + tx_stb_i <= '0'; + wait for 400 us; + tx_stb_i <= '1'; + tx_dat_i <= x"34"; + wait until rising_edge(clk_i); + wait until rising_edge(clk_i); + assert tx_ack_o = '0' report "TX accepted extra byte" severity error; + wait until tx_ack_o = '1'; + wait until rising_edge(clk_i); + tx_stb_i <= '0'; + wait for 1 ms; + + -- Attempt to transmit while receiving (waits until finished) + tx_byte <= x"56"; + wait for 400 us; + wait until falling_edge(clk_i); + tx_stb_i <= '1'; + tx_dat_i <= x"78"; + wait until rx_stb_o = '1'; + wait for 200 us; + wait until falling_edge(clk_i); + rx_ack_i <= '1'; + wait until rising_edge(clk_i); + wait until rising_edge(clk_i); + tx_stb_i <= '0'; + wait for 1 ms; + + -- Assert inhibit during transmit (finishes transmit) + wait until falling_edge(clk_i); + tx_stb_i <= '1'; + tx_dat_i <= x"9a"; + wait until rising_edge(clk_i); + tx_stb_i <= '0'; + wait for 400 us; + inhibit <= '1'; + wait for 1 ms; + + -- Attempt to transmit while inhibited (waits until uninhibited) + wait until falling_edge(clk_i); + tx_stb_i <= '1'; + tx_dat_i <= x"bc"; + wait until rising_edge(clk_i); + assert tx_ack_o = '0' report "TX ACKed while inhibited" severity error; + wait for 200 us; + inhibit <= '0'; + wait until tx_ack_o = '1'; + wait until rising_edge(clk_i); + tx_stb_i <= '0'; + wait for 1 ms; + + -- Inhibit while receiving aborts device + tx_byte <= x"de"; + wait for 400 us; + inhibit <= '1'; + wait for 200 us; + inhibit <= '0'; + wait for 400 us; + + -- Inhibit during stop bit waits to finish then inhibits + tx_byte <= x"f0"; + wait for 620 us; -- This should put it into the 11th clock + inhibit <= '1'; + wait until rx_stb_o = '1'; + rx_ack_i <= '1'; + wait until rising_edge(clk_i); + rx_ack_i <= '0'; + inhibit <= '0'; + wait for 400 us; + + -- Auto-inhibit inhibits device after reception until acknowledgement + auto_inhibit <= '1'; + tx_byte <= x"41"; + wait for 1 ms; + assert rx_stb_o = '1'; + assert ps2_clk = '0'; + wait until falling_edge(clk_i); + rx_ack_i <= '1'; + wait until rising_edge(clk_i); + rx_ack_i <= '0'; + wait for 200 us; + + wait; + end process; + + + uut: entity work.ps2_host + port map ( + rst_i => rst_i, + clk_i => clk_i, + tx_stb_i => tx_stb_i, + tx_ack_o => tx_ack_o, + tx_dat_i => tx_dat_i, + rx_stb_o => rx_stb_o, + rx_ack_i => rx_ack_i, + rx_dat_o => rx_dat_o, + inhibit => inhibit, + auto_inhibit => auto_inhibit, + tx_err_nack => tx_err_nack, + tx_err_timeout => tx_err_timeout, + rx_err_timeout => rx_err_timeout, + rx_err_missed => rx_err_missed, + rx_err_parity => rx_err_parity, + rx_err_start => rx_err_start, + rx_err_stop => rx_err_stop, + ps2_clk => ps2_clk, + ps2_data => ps2_data + ); + + device: entity simulated.ps2_device + port map ( + tx_byte => tx_byte, + + ps2_clk => ps2_clk, + ps2_data => ps2_data + ); + + ps2_clk <= 'H'; + ps2_data <= 'H'; + + clk_i_process :process + begin + clk_i <= '0'; + wait for clk_i_period/2; + clk_i <= '1'; + wait for clk_i_period/2; + end process; + +end; diff --git a/libraries/ps2/tests/test_ps2_host_wb.vhd b/libraries/ps2/tests/test_ps2_host_wb.vhd new file mode 100644 index 0000000..643005b --- /dev/null +++ b/libraries/ps2/tests/test_ps2_host_wb.vhd @@ -0,0 +1,312 @@ +library ieee; +use ieee.std_logic_1164.all; + +library simulated; + + +entity test_ps2_host_wb is +end test_ps2_host_wb; + + +architecture behavior of test_ps2_host_wb is + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal cyc_i: std_logic; + signal stb_i: std_logic; + signal we_i: std_logic; + signal ack_o: std_logic; + signal adr_i: std_logic_vector(2 downto 0); + signal dat_i: std_logic_vector(7 downto 0); + signal dat_o: std_logic_vector(7 downto 0); + + signal rx_ready: std_logic; + signal rx_full: std_logic; + signal tx_ready: std_logic; + signal tx_empty: std_logic; + + signal err_nack: std_logic; + signal err_txto: std_logic; + signal err_rxto: std_logic; + signal err_missed: std_logic; + signal err_parity: std_logic; + signal err_framing: std_logic; + + signal ps2_clk: std_logic; + signal ps2_data: std_logic; + + -- Simulated device interface + signal tx_byte: std_logic_vector(7 downto 0); + + type test_t is ( + NONE, + FIFOVER_S, FIFOVER_R, + AUTOHIB + ); + signal test_name: test_t := NONE; + +begin + + p_test: process + begin + -- Initial signal values + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + adr_i <= "000"; + dat_i <= x"00"; + + -- Reset + rst_i <= '1'; + wait for 20 ns; + rst_i <= '0'; + + -- Unmask signals + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '1'; + adr_i <= "001"; + dat_i <= x"ff"; + wait until rising_edge(clk_i); + while ack_o = '0' loop + wait until rising_edge(clk_i); + end loop; + wait for 10 ns; + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + + -- Enable interface + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '1'; + adr_i <= "000"; + dat_i <= x"01"; + wait until rising_edge(clk_i); + while ack_o = '0' loop + wait until rising_edge(clk_i); + end loop; + wait for 10 ns; + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + + -- Transmit 0xa5 + --cyc_i <= '1'; + --stb_i <= '1'; + --we_i <= '1'; + --adr_i <= "101"; + --dat_i <= x"a5"; + --wait until rising_edge(clk_i); + --while ack_o = '0' loop + -- wait until rising_edge(clk_i); + --end loop; + --wait for 10 ns; + --cyc_i <= '0'; + --stb_i <= '0'; + --we_i <= '0'; + + -- Receive 0x1c, 0xf0, 0x1c ('a' keypress) + wait for 1 ms; + tx_byte <= x"1c"; + wait for 800 us; + tx_byte <= x"f0"; + wait for 800 us; + tx_byte <= x"1c"; + wait for 1 ms; + + -- Retrieve keypresses + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "100"; + wait until rising_edge(clk_i); + wait until falling_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + wait for 10 us; + + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "100"; + wait until rising_edge(clk_i); + wait until falling_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + wait for 10 us; + + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "100"; + wait until rising_edge(clk_i); + wait until falling_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + wait for 10 us; + + wait for 200 us; + + -- Receive just too damn many things and overflow the FIFO + test_name <= FIFOVER_S; + tx_byte <= x"12"; + wait for 800 us; + tx_byte <= x"34"; + wait for 800 us; + tx_byte <= x"56"; + wait for 800 us; + tx_byte <= x"78"; + wait for 800 us; + tx_byte <= x"9a"; + wait for 1 ms; + + -- Retrieve keypresses + test_name <= FIFOVER_R; + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "100"; + wait until rising_edge(clk_i); + wait until falling_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + wait for 10 us; + + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "100"; + wait until rising_edge(clk_i); + wait until falling_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + wait for 10 us; + + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "100"; + wait until rising_edge(clk_i); + wait until falling_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + wait for 10 us; + + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "100"; + wait until rising_edge(clk_i); + wait until falling_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + wait for 10 us; + + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '0'; + adr_i <= "100"; + wait until rising_edge(clk_i); + wait until falling_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + wait for 10 us; + + wait for 200 us; + + -- Test auto-inhibit + test_name <= AUTOHIB; + + wait until falling_edge(clk_i); + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '1'; + adr_i <= "000"; + dat_i <= x"41"; + wait until rising_edge(clk_i); + while ack_o = '0' loop + wait until rising_edge(clk_i); + end loop; + wait for 10 ns; + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + wait for 200 us; + + tx_byte <= x"12"; + wait for 800 us; + tx_byte <= x"34"; + wait for 800 us; + tx_byte <= x"56"; + wait for 800 us; + tx_byte <= x"78"; + wait for 800 us; + tx_byte <= x"9a"; + wait for 1 ms; + + wait; + end process; + + -- Simulated device + e_simdev: entity simulated.ps2_device + port map ( + tx_byte => tx_byte, + + ps2_clk => ps2_clk, + ps2_data => ps2_data + ); + + -- Pull-up resistors + ps2_clk <= 'H'; + ps2_data <= 'H'; + + e_uut: entity work.ps2_host_wb + port map ( + rst_i => rst_i, + clk_i => clk_i, + + cyc_i => cyc_i, + stb_i => stb_i, + we_i => we_i, + ack_o => ack_o, + adr_i => adr_i, + dat_i => dat_i, + dat_o => dat_o, + + rx_ready => rx_ready, + rx_full => rx_full, + tx_ready => tx_ready, + tx_empty => tx_empty, + + err_nack => err_nack, + err_txto => err_txto, + err_rxto => err_rxto, + err_missed => err_missed, + err_parity => err_parity, + err_framing => err_framing, + + ps2_clk => ps2_clk, + ps2_data => ps2_data + ); + + p_clk: process + begin + clk_i <= '0'; + wait for 10 ns; + clk_i <= '1'; + wait for 10 ns; + end process; + +end; diff --git a/libraries/rs232/rs232_rx.vhd b/libraries/rs232/rs232_rx.vhd new file mode 100644 index 0000000..5090052 --- /dev/null +++ b/libraries/rs232/rs232_rx.vhd @@ -0,0 +1,231 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.std_logic_misc.all; + + +entity rs232_rx is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_o: out std_logic; + ack_i: in std_logic; + dat_o: out std_logic_vector(7 downto 0); + + divisor_in: in std_logic_vector(15 downto 0); + parity_in: in std_logic; -- Expect a parity bit + + missed_out: out std_logic; + parity_out: out std_logic; + + rx_in: in std_logic + ); +end rs232_rx; + + +architecture behavioral of rs232_rx is + + type state_t is (S_READY, S_START, S_DATA, S_PARITY, S_STOP); + signal state_reg: state_t; + signal next_state: state_t; + + signal latch_en: std_logic; + signal ready_out_reg: std_logic; + signal missed_out_reg: std_logic; + signal parity_out_reg: std_logic; + signal data_out_reg: std_logic_vector(7 downto 0); + + signal bit_count_done: std_logic; + signal bit_count_reg: unsigned(3 downto 0); + signal shift_en: std_logic; + signal shifter_reg: std_logic_vector(7 downto 0); + signal parity_en: std_logic; + signal parity_reg: std_logic; + + signal baud_gen_en: std_logic; + signal baud_counter: unsigned(15 downto 0); + signal next_bit: std_logic; + signal half_bit: std_logic; + +begin + + ---------------------------------------------------------------------------- + -- State machine + + process (rst_i, clk_i, next_state) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + state_reg <= S_READY; + else + state_reg <= next_state; + end if; + end if; + end process; + + process (state_reg, rx_in, parity_in, half_bit, next_bit, bit_count_done) + begin + -- Default to remaining in current state + next_state <= state_reg; + + -- Default to idling other hardware + shift_en <= '0'; + parity_en <= '0'; + baud_gen_en <= '0'; + latch_en <= '0'; + + case state_reg is + when S_READY => + if rx_in = '0' then + next_state <= S_START; + end if; + + when S_START => + baud_gen_en <= '1'; + if next_bit = '1' then + next_state <= S_DATA; + end if; + + when S_DATA => + baud_gen_en <= '1'; + shift_en <= '1'; + if next_bit = '1' and bit_count_done = '1' then + if parity_in = '1' then + next_state <= S_PARITY; + else + latch_en <= '1'; + next_state <= S_STOP; + end if; + end if; + + when S_PARITY => + baud_gen_en <= '1'; + parity_en <= '1'; + if next_bit = '1' then + latch_en <= '1'; + next_state <= S_STOP; + end if; + + when S_STOP => + baud_gen_en <= '1'; + if next_bit = '1' then + next_state <= S_READY; + end if; + + when others => + next_state <= S_READY; + end case; + end process; + + + ---------------------------------------------------------------------------- + -- Control interface + + process (rst_i, clk_i, ack_i, latch_en, ready_out_reg, parity_reg, shifter_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + missed_out_reg <= '0'; + ready_out_reg <= '0'; + parity_out_reg <= '0'; + data_out_reg <= (others => '0'); + else + if ack_i = '1' then + missed_out_reg <= '0'; + ready_out_reg <= '0'; + end if; + + -- Separate if statement so a new value can be latched during + -- the same cycle that a value is acknowledged + if latch_en = '1' then + missed_out_reg <= ready_out_reg; + ready_out_reg <= '1'; + parity_out_reg <= parity_reg; + data_out_reg <= shifter_reg; + end if; + end if; + end if; + end process; + + stb_o <= ready_out_reg; + missed_out <= missed_out_reg; + parity_out <= parity_out_reg; + dat_o <= data_out_reg; + + + ---------------------------------------------------------------------------- + -- Shift register, bit counter, and parity checker + + process (rst_i, clk_i, shift_en, half_bit, rx_in) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + shifter_reg <= (others => '0'); + else + if shift_en = '1' and half_bit = '1' then + shifter_reg <= rx_in & shifter_reg(shifter_reg'high downto 1); + end if; + end if; + end if; + end process; + + process (rst_i, clk_i, latch_en, shift_en, parity_en, half_bit, rx_in, parity_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + parity_reg <= '0'; + else + if latch_en = '1' then + parity_reg <= '0'; + elsif (shift_en = '1' or parity_en = '1') and half_bit = '1' then + parity_reg <= parity_reg xor rx_in; + end if; + end if; + end if; + end process; + + process (rst_i, clk_i, shift_en, bit_count_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + bit_count_reg <= x"8"; + else + if shift_en = '0' then + bit_count_reg <= x"8"; + elsif half_bit = '1' then + bit_count_reg <= bit_count_reg - 1; + end if; + end if; + end if; + end process; + + bit_count_done <= '1' when or_reduce(std_logic_vector(bit_count_reg)) = '0' else '0'; + + + ---------------------------------------------------------------------------- + -- Baud rate generator and bit timer + + baud_gen: process (rst_i, clk_i, baud_gen_en, next_bit) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + baud_counter <= (others => '0'); + else + if baud_gen_en = '1' then + if next_bit = '1' then + baud_counter <= (others => '0'); + else + baud_counter <= baud_counter + 1; + end if; + else + baud_counter <= (others => '0'); + end if; + end if; + end if; + end process; + + next_bit <= '1' when std_logic_vector(baud_counter) = divisor_in else '0'; + half_bit <= '1' when std_logic_vector(baud_counter) = '0' & divisor_in(divisor_in'high downto 1) else '0'; + +end behavioral; diff --git a/libraries/rs232/rs232_tx.vhd b/libraries/rs232/rs232_tx.vhd new file mode 100644 index 0000000..7aa872a --- /dev/null +++ b/libraries/rs232/rs232_tx.vhd @@ -0,0 +1,207 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.std_logic_misc.all; + + +entity rs232_tx is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + ack_o: out std_logic; + dat_i: in std_logic_vector(7 downto 0); + + parity_en_in: in std_logic; + parity_odd_in: in std_logic; + stop_bits_in: in std_logic; -- '0'->1 stop bit, '1'->2 stop bits + divisor_in: in std_logic_vector(15 downto 0); + + tx_out: out std_logic + ); +end rs232_tx; + + +architecture behavioral of rs232_tx is + + type state_t is (S_READY, S_START, S_DATA, S_PARITY, S_STOP_1, S_STOP_2); + signal state_reg: state_t; + signal next_state: state_t; + + signal bit_count_done: std_logic; + signal bit_count_reg: unsigned(3 downto 0); + signal shift_en: std_logic; + signal shifter_reg: std_logic_vector(7 downto 0); + signal parity_reg: std_logic; + + signal baud_gen_en: std_logic; + signal baud_counter: unsigned(15 downto 0); + signal next_bit: std_logic; + +begin + + ---------------------------------------------------------------------------- + -- State machine + + process (rst_i, clk_i, next_state) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + state_reg <= S_READY; + else + state_reg <= next_state; + end if; + end if; + end process; + + process (state_reg, stb_i, parity_en_in, next_bit, bit_count_done, shifter_reg, parity_reg, stop_bits_in) + begin + -- Default to remaining in current state + next_state <= state_reg; + + -- Default to idling other hardware + shift_en <= '0'; + baud_gen_en <= '0'; + + -- Default to idle tx line output + tx_out <= '1'; + + -- Default to not ready + ack_o <= '0'; + + case state_reg is + when S_READY => + ack_o <= '1'; + if stb_i = '1' then + next_state <= S_START; + end if; + + when S_START => + baud_gen_en <= '1'; + tx_out <= '0'; + if next_bit = '1' then + next_state <= S_DATA; + end if; + + when S_DATA => + baud_gen_en <= '1'; + shift_en <= '1'; + tx_out <= shifter_reg(0); + if next_bit = '1' and bit_count_done = '1' then + if parity_en_in = '1' then + next_state <= S_PARITY; + else + if stop_bits_in = '1' then + next_state <= S_STOP_1; + else + next_state <= S_STOP_2; + end if; + end if; + end if; + + when S_PARITY => + baud_gen_en <= '1'; + tx_out <= parity_reg; + if next_bit = '1' then + if stop_bits_in = '1' then + next_state <= S_STOP_1; + else + next_state <= S_STOP_2; + end if; + end if; + + when S_STOP_1 => + baud_gen_en <= '1'; + if next_bit = '1' then + next_state <= S_STOP_2; + end if; + + when S_STOP_2 => + baud_gen_en <= '1'; + if next_bit = '1' then + next_state <= S_READY; + end if; + + when others => + next_state <= S_READY; + end case; + end process; + + + ---------------------------------------------------------------------------- + -- Shift register, bit counter, and parity generator + + process (rst_i, clk_i, stb_i, shift_en, next_bit, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + shifter_reg <= (others => '0'); + else + if stb_i = '1' then + shifter_reg <= dat_i; + elsif shift_en = '1' and next_bit = '1' then + shifter_reg <= '1' & shifter_reg(shifter_reg'high downto 1); + end if; + end if; + end if; + end process; + + process (rst_i, clk_i, stb_i, parity_odd_in, parity_reg, shifter_reg, shift_en, next_bit) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + parity_reg <= '0'; + else + if stb_i = '1' then + parity_reg <= parity_odd_in; + elsif shift_en = '1' and next_bit = '1' then + parity_reg <= parity_reg xor shifter_reg(0); + end if; + end if; + end if; + end process; + + process (rst_i, clk_i, shift_en, next_bit) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + bit_count_reg <= x"8"; + else + if shift_en = '0' then + bit_count_reg <= x"8"; + elsif next_bit = '1' then + bit_count_reg <= bit_count_reg - 1; + end if; + end if; + end if; + end process; + + bit_count_done <= '1' when bit_count_reg = 0 else '0'; + + + ---------------------------------------------------------------------------- + -- Baud rate generator and bit timer + + baud_gen: process (rst_i, clk_i, baud_gen_en, next_bit) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + baud_counter <= (others => '0'); + else + if baud_gen_en = '1' then + if next_bit = '1' then + baud_counter <= (others => '0'); + else + baud_counter <= baud_counter + 1; + end if; + else + baud_counter <= (others => '0'); + end if; + end if; + end if; + end process; + + next_bit <= '1' when baud_counter = unsigned(divisor_in) else '0'; + +end behavioral; diff --git a/libraries/rs232/rs232_uart.vhd b/libraries/rs232/rs232_uart.vhd new file mode 100644 index 0000000..ba6b3e9 --- /dev/null +++ b/libraries/rs232/rs232_uart.vhd @@ -0,0 +1,347 @@ +-------------------------------------------------------------------------------- +-- TODO: https://eecs.umich.edu/courses/doing_dsp/handout/SRL16E.pdf +-- Play with using shift-registers to make this more dense +-- +-- +---+---+---+---+---+---+---+---+ +-- | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +-- +---+---+---+---+---+---+---+---+ +-- 0 |LB |BRK| |STP|POD|PE |TXE|RXE| CTRL +-- +---+-------+---+---+---+---+---+ +-- 1 | DIVISOR LOW BYTE | BAUDL +-- +-------------------------------+ +-- 2 | DIVISOR HIGH BYTE | BAUDH +-- +---+---+---+---+---+---+---+---+ +-- 3 |BER|FER|PER|OER|TXE|TXR|RXF|RXR| IMASK +-- +---+---+---+---+---+---+---+---+ +-- 4 |BER|FER|PER|OER|TXE|TXR|RXF|RXR| IFLAG +-- +---+---+---+---+---+---+---+---+ +-- 5 | DATA | DATA +-- +-------------------------------+ +-- 6 | (RESERVED) | +-- +-------------------------------+ +-- 7 | (RESERVED) | +-- +-------------------------------+ +-- +-- CTRL register bits: +-- RXE - Enable receive logic +-- TXE - Enable transmit logic +-- PE - Enable parity generation and checking +-- POD - Generate and check for odd parity (else even) +-- STP - Number of stop bits: clear -> 1, set -> 2 +-- BRK - Create break condition by holding TX line low (not yet implemented) +-- LB - Loopback enable +-- +-- IMASK/IFLAG register bits: +-- RXR - Receiver has at least one byte ready +-- Deasserts when all DATA read +-- RXF - Receiver FIFO is full +-- Deasserts on read from DATA +-- TXR - Transmitter ready to accept at least one byte +-- Deasserts when TX FIFO is full +-- TXE - Transmitter FIFO is empty +-- Deasserts on write to DATA +-- OER - Receiver FIFO overrun, at least one byte was lost due to lack of space +-- Write one to clear +-- PER - Receiver parity error +-- Write one to clear +-- FER - Receiver framing error, either start or stop bit was incorrect +-- Write one to clear +-- (not yet implemented) +-- BER - Break condition detected (RX line was held low for a full byte) +-- Write one to clear +-- (not yet implemented) +-- +-- BAUDL/BAUDH: +-- 16-bit clock divisor representing the number of system clock cycles per bit +-- +-- For 50MHz clock: +-- +------+---------+ +-- | Baud | Divisor | +-- +------+---------+ +-- | 9600 | 0x1458 | +-- +------+---------+ +-- +-- Notes: +-- +-- User must check flags before reading/writing DATA register to ensure data is +-- available in RX or space is available in TX, otherwise undefined data will be +-- returned or written data will be lost. +-- +-- TODO: Use generics for FIFO capacities +-- TODO: Fill in baud divisor table for more rates +-- TODO: Detect and generate break conditions +-- TODO: Add framing checks +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +library utility; +library work; + + +entity rs232_uart is + port ( + -- Wishbone SYSCON + rst_i: in std_logic; + clk_i: in std_logic; + + -- Wishbone bus slave interface + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(2 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + -- Queueing signals + rx_ready: out std_logic; -- At least one byte ready in the RX FIFO + rx_full: out std_logic; -- The RX FIFO is full + tx_ready: out std_logic; -- At least one byte free in the TX FIFO + tx_empty: out std_logic; -- The TX FIFO is empty + + -- Error signals + err_break: out std_logic; -- Break condition detected + err_framing: out std_logic; -- RX stop bit incorrect + err_parity: out std_logic; -- RX parity incorrect + err_overflow: out std_logic; -- Byte received with no space in queue + + -- RS232 interface + tx: out std_logic; + rx: in std_logic + ); +end rs232_uart; + + +architecture behavioral of rs232_uart is + + -- Register addresses + constant ADDR_CTRL: std_logic_vector(2 downto 0) := "000"; + constant ADDR_BAUDL: std_logic_vector(2 downto 0) := "001"; + constant ADDR_BAUDH: std_logic_vector(2 downto 0) := "010"; + constant ADDR_IMASK: std_logic_vector(2 downto 0) := "011"; + constant ADDR_IFLAG: std_logic_vector(2 downto 0) := "100"; + constant ADDR_DATA: std_logic_vector(2 downto 0) := "101"; + + -- Registers + signal ctrl_reg: std_logic_vector(7 downto 0); + signal rx_en: std_logic; + signal tx_en: std_logic; + signal parity_en: std_logic; + signal parity_odd: std_logic; + signal stop_bits: std_logic; + signal do_break: std_logic; + signal loopback: std_logic; + + signal divisor_reg: std_logic_vector(15 downto 0); + + signal imask_reg: std_logic_vector(7 downto 0); + signal iflag_reg: std_logic_vector(7 downto 0); + + signal tx_internal: std_logic; + signal rx_internal: std_logic; + + signal err_missed_int: std_logic; + signal err_parity_int: std_logic; + + signal txqh_stb: std_logic; + signal txqh_ack: std_logic; + --signal txqh_dat: std_logic; -- Comes from dat_i + signal txqt_stb: std_logic; + signal txqt_ack: std_logic; + signal txqt_dat: std_logic_vector(7 downto 0); + signal txq_empty: std_logic; + signal txq_full: std_logic; + + signal rxqh_stb: std_logic; + signal rxqh_ack: std_logic; + signal rxqh_dat: std_logic_vector(7 downto 0); + signal rxqt_stb: std_logic; + signal rxqt_ack: std_logic; + signal rxqt_dat: std_logic_vector(7 downto 0); + signal rxq_empty: std_logic; + signal rxq_full: std_logic; + + signal data_stb: std_logic; + signal rst_tx: std_logic; + signal rst_rx: std_logic; + +begin + + -- Wishbone bus + process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + ctrl_reg <= (others => '0'); + divisor_reg <= (others => '0'); + imask_reg <= (others => '0'); + iflag_reg <= (others => '0'); + else + if cyc_i = '1' and stb_i = '1' and we_i = '1' then + case adr_i is + when ADDR_CTRL => ctrl_reg <= dat_i; + when ADDR_BAUDL => divisor_reg( 7 downto 0) <= dat_i; + when ADDR_BAUDH => divisor_reg(15 downto 8) <= dat_i; + when ADDR_IMASK => imask_reg <= dat_i; + when ADDR_IFLAG => iflag_reg <= iflag_reg and (not dat_i); + when ADDR_DATA => null; + when others => null; + end case; + end if; + + -- Latch interrupt flags + iflag_reg(0) <= rxqt_stb; -- using !rxq_empty will trigger interrupt before data makes it through the pipeline FIFO + iflag_reg(1) <= rxq_full; + iflag_reg(2) <= not txq_full; + iflag_reg(3) <= txq_empty; + + if rxqh_stb = '1' then + iflag_reg(4) <= iflag_reg(4) or err_missed_int; -- overflow + iflag_reg(5) <= iflag_reg(5) or err_parity_int; -- parity + --iflag_reg(6) <= iflag_reg(6) or ; -- framing + --iflag_reg(7) <= iflag_reg(7) or ; -- break + end if; + + end if; + end if; + end process; + + -- Separate strobe signal for data register access + data_stb <= (cyc_i and stb_i) when adr_i = ADDR_DATA else '0'; + + -- Deliver data stroe to TX or RX based on transaction direction + txqh_stb <= data_stb when we_i = '1' else '0'; + rxqt_ack <= data_stb when we_i = '0' else '0'; + + -- All registers respond to the host immediately + -- If no data is available for data reads or no space is available for data + -- writes then the transaction gets eaten. It's up to the user to check the + -- flags before attempting data access. + ack_o <= '1'; + + -- Output multiplexer + with adr_i select dat_o <= + ctrl_reg when ADDR_CTRL, + divisor_reg( 7 downto 0) when ADDR_BAUDL, + divisor_reg(15 downto 8) when ADDR_BAUDH, + imask_reg when ADDR_IMASK, + iflag_reg when ADDR_IFLAG, + rxqt_dat when ADDR_DATA, + (others => '1') when others; + + -- Internal control signals + rx_en <= ctrl_reg(0); + tx_en <= ctrl_reg(1); + parity_en <= ctrl_reg(2); + parity_odd <= ctrl_reg(3); + stop_bits <= ctrl_reg(4); + do_break <= ctrl_reg(6); + loopback <= ctrl_reg(7); + + -- Interrupt out signals + rx_ready <= iflag_reg(0) and imask_reg(0); + rx_full <= iflag_reg(1) and imask_reg(1); + tx_ready <= iflag_reg(2) and imask_reg(2); + tx_empty <= iflag_reg(3) and imask_reg(3); + err_overflow <= iflag_reg(4) and imask_reg(4); + err_parity <= iflag_reg(5) and imask_reg(5); + err_framing <= iflag_reg(6) and imask_reg(6); + err_break <= iflag_reg(7) and imask_reg(7); + + -- Input-output logic + tx <= '1' when tx_en = '0' + else '0' when do_break = '1' + else tx_internal; + + rx_internal <= '1' when rx_en = '0' + else tx_internal when loopback = '1' + else rx; + + -- Also reset TX/RX logic when each are disabled + rst_tx <= rst_i or (not tx_en); + rst_rx <= rst_i or (not tx_en); + + -- Transmitter + e_tx: entity work.rs232_tx + port map ( + rst_i => rst_tx, + clk_i => clk_i, + + stb_i => txqt_stb, + ack_o => txqt_ack, + dat_i => txqt_dat, + + parity_en_in => parity_en, + parity_odd_in => parity_odd, + stop_bits_in => stop_bits, + divisor_in => divisor_reg, + + tx_out => tx_internal + ); + + -- Transmit FIFO + e_tx_fifo: entity utility.fifo + generic map ( + WIDTH => 8, + COUNT => 16 + ) + port map ( + rst_i => rst_tx, + clk_i => clk_i, + + h_stb_i => txqh_stb, + h_ack_o => txqh_ack, + h_dat_i => dat_i, + + t_stb_o => txqt_stb, + t_ack_i => txqt_ack, + t_dat_o => txqt_dat, + + is_empty => txq_empty, + is_full => txq_full + ); + + -- Receiver + e_rx: entity work.rs232_rx + port map ( + rst_i => rst_rx, + clk_i => clk_i, + + stb_o => rxqh_stb, + ack_i => rxqh_ack, + dat_o => rxqh_dat, + + divisor_in => divisor_reg, + parity_in => parity_en, + + missed_out => err_missed_int, + parity_out => err_parity_int, + + rx_in => rx_internal + ); + + -- Receive FIFO + e_rx_fifo: entity utility.fifo + generic map ( + WIDTH => 8, + COUNT => 16 + ) + port map ( + rst_i => rst_rx, + clk_i => clk_i, + + h_stb_i => rxqh_stb, + h_ack_o => rxqh_ack, + h_dat_i => rxqh_dat, + + t_stb_o => rxqt_stb, + t_ack_i => rxqt_ack, + t_dat_o => rxqt_dat, + + is_empty => rxq_empty, + is_full => rxq_full + ); + +end behavioral; diff --git a/libraries/rs232/tests/program.sh b/libraries/rs232/tests/program.sh new file mode 100755 index 0000000..e2092f0 --- /dev/null +++ b/libraries/rs232/tests/program.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +djtgcfg -d Nexys2 -i 0 -f test_nexys2.bit prog + diff --git a/libraries/rs232/tests/test_nexys2.vhd b/libraries/rs232/tests/test_nexys2.vhd new file mode 100644 index 0000000..1c16dba --- /dev/null +++ b/libraries/rs232/tests/test_nexys2.vhd @@ -0,0 +1,85 @@ +library ieee; +use ieee.std_logic_1164.all; + +library nexys2_lib; +library work; + + +entity test_nexys2 is + port ( + clk_50: in std_logic; + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + Led: out std_logic_vector(7 downto 0); + sw: in std_logic_vector(7 downto 0); + btn: in std_logic_vector(3 downto 0); + RsRx: in std_logic; + RsTx: out std_logic + ); +end test_nexys2; + + +architecture behavioral of test_nexys2 is + + signal rst: std_logic; + signal clk: std_logic; + + signal cyc: std_logic; + signal stb: std_logic; + signal we: std_logic; + signal ack: std_logic; + signal adr: std_logic_vector(7 downto 0); + signal dat_mosi: std_logic_vector(7 downto 0); + signal dat_miso: std_logic_vector(7 downto 0); + +begin + + e_uut: entity work.rs232_uart + port map ( + rst_i => rst, + clk_i => clk, + + cyc_i => cyc, + stb_i => stb, + we_i => we, + ack_o => ack, + adr_i => adr(2 downto 0), + dat_i => dat_mosi, + dat_o => dat_miso, + + rx_ready => Led(0), + rx_full => Led(1), + tx_ready => Led(2), + tx_empty => Led(3), + err_break => Led(4), + err_framing => Led(5), + err_parity => Led(6), + err_overflow => Led(7), + + tx => RsTx, + rx => RsRx + ); + + e_wb: entity nexys2_lib.wishbone_debugger + port map ( + clk_o => clk, + rst_o => rst, + + cyc_o => cyc, + stb_o => stb, + we_o => we, + ack_i => ack, + adr_o => adr, + dat_o => dat_mosi, + dat_i => dat_miso, + + clk_50 => clk_50, + seg => seg, + dp => dp, + an => an, + sw => sw, + btn => btn + ); + +end behavioral; diff --git a/libraries/rs232/tests/test_rs232_rx.vhd b/libraries/rs232/tests/test_rs232_rx.vhd new file mode 100644 index 0000000..cc61842 --- /dev/null +++ b/libraries/rs232/tests/test_rs232_rx.vhd @@ -0,0 +1,240 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_arith.all; +use ieee.std_logic_unsigned.all; +use ieee.std_logic_misc.all; + +library work; + + +entity test_rs232_rx is +end test_rs232_rx; + + +architecture behavioral of test_rs232_rx is + + type tx_state_t is ( + S_TX_INIT, + S_TX_A5, S_TX_A5_DONE, + S_TX_C2, S_TX_C2_DONE, + S_TX_FF, S_TX_FF_DONE, + S_TX_00, S_TX_00_DONE, + S_TX_DONE + ); + + signal tx_state: tx_state_t; + + signal clk_50M: std_logic; + signal rst: std_logic; + + signal uut_rx: std_logic; + + signal ready: std_logic; + signal missed: std_logic; + signal ack: std_logic; + signal parity: std_logic; + signal data: std_logic_vector(7 downto 0); + +begin + + -- 50MHz clock + p_clock_50M: process + begin + clk_50M <= '0'; + wait for 10 ns; + clk_50M <= '1'; + wait for 10 ns; + end process; + + + -- UART Rx block to test + uut: entity work.rs232_rx + port map ( + rst_i => rst, + clk_i => clk_50M, + + stb_o => ready, + ack_i => ack, + dat_o => data, + + divisor_in => x"1458", + parity_in => '0', + + missed_out => missed, + parity_out => parity, + + rx_in => uut_rx + ); + + + -- Test receive + test_rx: process + begin + ack <= '0'; + + rst <= '1'; + wait until rising_edge(clk_50M); + wait until rising_edge(clk_50M); + rst <= '0'; + + wait until ready = '1'; + assert data = x"a5" report "Failed to receive A5"; + assert missed = '0' report "Spurrious miss reported"; + ack <= '1'; + wait for 30 ns; + ack <= '0'; + assert ready = '0'; + assert missed = '0'; + assert parity = '0'; + + wait until ready = '1'; + assert data = x"c2"; + assert missed = '0'; + assert parity = '1'; + + wait until tx_state = S_TX_FF_DONE; + assert data = x"ff"; + assert ready = '1'; + assert missed = '1'; + ack <= '1'; + wait for 30 ns; + ack <= '0'; + + wait until ready = '1'; + assert data = x"00"; + assert missed = '0'; + ack <= '1'; + wait for 30 ns; + ack <= '0'; + + wait; + end process; + + + -- RS232 data being sent to the UUT + p_data_in: process + type parity_t is (P_NONE, P_EVEN, P_ODD); + + -- Parameters + constant baud_rate: integer := 9600; + constant start_bits: real := 1.0; + constant data_bits: integer := 8; + constant parity: parity_t := P_NONE; + constant stop_bits: real := 1.0; + + -- Derived constants + constant bit_time: time := (1000 ms / baud_rate); + + variable tx_value: std_logic_vector(data_bits-1 downto 0); + begin + -- Setup + tx_state <= S_TX_INIT; + uut_rx <= '1'; + wait for bit_time * 2; + + -- Transmit A5 + tx_state <= S_TX_A5; + tx_value := x"a5"; + + uut_rx <= '0'; + wait for bit_time * start_bits; + for i in 0 to data_bits-1 loop + uut_rx <= tx_value(i); + wait for bit_time; + end loop; + case parity is + when P_EVEN => + uut_rx <= xor_reduce(tx_value); + wait for bit_time; + when P_ODD => + uut_rx <= xor_reduce(tx_value) xor '1'; + wait for bit_time; + when others => + null; + end case; + uut_rx <= '1'; + wait for bit_time * stop_bits; + tx_state <= S_TX_A5_DONE; + wait for 20 ns; + + -- Transmit C3 + tx_state <= S_TX_C2; + tx_value := x"c2"; + + uut_rx <= '0'; + wait for bit_time * start_bits; + for i in 0 to data_bits-1 loop + uut_rx <= tx_value(i); + wait for bit_time; + end loop; + case parity is + when P_EVEN => + uut_rx <= xor_reduce(tx_value); + wait for bit_time; + when P_ODD => + uut_rx <= xor_reduce(tx_value) xor '1'; + wait for bit_time; + when others => + null; + end case; + uut_rx <= '1'; + wait for bit_time * stop_bits; + tx_state <= S_TX_C2_DONE; + wait for 20 ns; + + -- Transmit FF + tx_state <= S_TX_FF; + tx_value := x"ff"; + + uut_rx <= '0'; + wait for bit_time * start_bits; + for i in 0 to data_bits-1 loop + uut_rx <= tx_value(i); + wait for bit_time; + end loop; + case parity is + when P_EVEN => + uut_rx <= xor_reduce(tx_value); + wait for bit_time; + when P_ODD => + uut_rx <= xor_reduce(tx_value) xor '1'; + wait for bit_time; + when others => + null; + end case; + uut_rx <= '1'; + wait for bit_time * stop_bits; + tx_state <= S_TX_FF_DONE; + wait for 20 ns; + + -- Transmit 00 + tx_state <= S_TX_00; + tx_value := x"00"; + + uut_rx <= '0'; + wait for bit_time * start_bits; + for i in 0 to data_bits-1 loop + uut_rx <= tx_value(i); + wait for bit_time; + end loop; + case parity is + when P_EVEN => + uut_rx <= xor_reduce(tx_value); + wait for bit_time; + when P_ODD => + uut_rx <= xor_reduce(tx_value) xor '1'; + wait for bit_time; + when others => + null; + end case; + uut_rx <= '1'; + wait for bit_time * stop_bits; + tx_state <= S_TX_00_DONE; + wait for 20 ns; + + -- Done + tx_state <= S_TX_DONE; + wait; + end process p_data_in; + +end behavioral; diff --git a/libraries/rs232/tests/test_rs232_tx.vhd b/libraries/rs232/tests/test_rs232_tx.vhd new file mode 100644 index 0000000..a36c297 --- /dev/null +++ b/libraries/rs232/tests/test_rs232_tx.vhd @@ -0,0 +1,100 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.std_logic_misc.all; + +library work; + + +entity test_rs232_tx is +end test_rs232_tx; + + +architecture behavioral of test_rs232_tx is + + type tx_state_t is ( + S_TX_INIT, + S_TX_A5, S_TX_A5_DONE, + S_TX_C3, S_TX_C3_DONE, + S_TX_FF, S_TX_FF_DONE, + S_TX_00, S_TX_00_DONE, + S_TX_DONE + ); + + type test_state_t is ( + S_TEST_RESET, + S_TEST_DONE + ); + + signal tx_state: tx_state_t; + signal test_state: test_state_t; + + signal clk_50M: std_logic; + signal rst: std_logic; + + signal uut_tx: std_logic; + + signal ready: std_logic; + signal start: std_logic; + signal data: std_logic_vector(7 downto 0); + +begin + + -- 50MHz clock + p_clock_50M: process + begin + clk_50M <= '0'; + wait for 10 ns; + clk_50M <= '1'; + wait for 10 ns; + end process p_clock_50M; + + + -- UART Tx block to test + uut: entity work.rs232_tx + port map ( + rst_i => rst, + clk_i => clk_50M, + + stb_i => start, + ack_o => ready, + dat_i => data, + + parity_en_in => '0', + parity_odd_in => '0', + stop_bits_in => '0', + divisor_in => x"1458", + + tx_out => uut_tx + ); + + + -- Test transmit + test_tx: process + begin + start <= '0'; + data <= x"00"; + + rst <= '1'; + wait until rising_edge(clk_50M); + wait until rising_edge(clk_50M); + rst <= '0'; + + assert ready = '1'; + data <= x"a5"; + start <= '1'; + wait for 30 ns; + start <= '0'; + assert ready = '0'; + + wait until ready = '1'; + data <= x"c3"; + start <= '1'; + wait for 30 ns; + start <= '0'; + assert ready = '0'; + + wait; + end process; + +end behavioral; diff --git a/libraries/rs232/tests/test_uart.vhd b/libraries/rs232/tests/test_uart.vhd new file mode 100644 index 0000000..27f1145 --- /dev/null +++ b/libraries/rs232/tests/test_uart.vhd @@ -0,0 +1,180 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity test_uart is +end test_uart; + + +architecture behavior of test_uart is + + constant clk_i_period: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal cyc_i: std_logic; + signal stb_i: std_logic; + signal we_i: std_logic; + signal ack_o: std_logic; + signal adr_i: std_logic_vector(2 downto 0); + signal dat_i: std_logic_vector(7 downto 0); + signal dat_o: std_logic_vector(7 downto 0); + + signal rx_ready: std_logic; + signal rx_full: std_logic; + signal tx_ready: std_logic; + signal tx_empty: std_logic; + signal break_err: std_logic; + signal framing_err: std_logic; + signal parity_err: std_logic; + signal overflow_err: std_logic; + + signal rx: std_logic; + signal tx: std_logic; + +begin + + test: process + begin + -- Initial signal values + --rx <= '0'; + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + adr_i <= "000"; + dat_i <= x"00"; + + -- Reset + rst_i <= '1'; + wait for clk_i_period*2; + rst_i <= '0'; + + -- Configure + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '1'; + adr_i <= "001"; + dat_i <= x"58"; + if ack_o = '0' then wait until ack_o = '1'; end if; + wait until rising_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + adr_i <= "000"; + dat_i <= x"00"; + + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '1'; + adr_i <= "010"; + dat_i <= x"14"; + if ack_o = '0' then wait until ack_o = '1'; end if; + wait until rising_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + adr_i <= "000"; + dat_i <= x"00"; + + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '1'; + adr_i <= "000"; + dat_i <= "00000011"; + if ack_o = '0' then wait until ack_o = '1'; end if; + wait until rising_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + adr_i <= "000"; + dat_i <= x"00"; + + -- Transmit byte + cyc_i <= '1'; + stb_i <= '1'; + we_i <= '1'; + adr_i <= "101"; + dat_i <= x"A5"; + if ack_o = '0' then wait until ack_o = '1'; end if; + wait until rising_edge(clk_i); + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + adr_i <= "000"; + dat_i <= x"00"; + + wait; + end process; + + rs232: process + begin + rx <= '1'; + wait for 500 us; + + -- 1 start bit + rx <= '0'; + wait for 100 us; + + -- 8 data bits + rx <= '1'; + wait for 100 us; + rx <= '1'; + wait for 100 us; + rx <= '0'; + wait for 100 us; + rx <= '0'; + wait for 100 us; + rx <= '0'; + wait for 100 us; + rx <= '1'; + wait for 100 us; + rx <= '0'; + wait for 100 us; + rx <= '1'; + wait for 100 us; + + -- 0 parity bits + + -- 2 stop bits + rx <= '1'; + wait for 200 us; + + wait; + end process; + + uut: entity work.rs232_uart + port map ( + rst_i => rst_i, + clk_i => clk_i, + + cyc_i => cyc_i, + stb_i => stb_i, + we_i => we_i, + ack_o => ack_o, + adr_i => adr_i, + dat_i => dat_i, + dat_o => dat_o, + + rx_ready => rx_ready, + rx_full => rx_full, + tx_ready => tx_ready, + tx_empty => tx_empty, + break_err => break_err, + framing_err => framing_err, + parity_err => parity_err, + overflow_err => overflow_err, + + tx => tx, + rx => rx + ); + + clk_i_process: process + begin + clk_i <= '0'; + wait for clk_i_period/2; + clk_i <= '1'; + wait for clk_i_period/2; + end process; + +end; diff --git a/libraries/simulated/delay_edges.vhd b/libraries/simulated/delay_edges.vhd new file mode 100644 index 0000000..42ffd41 --- /dev/null +++ b/libraries/simulated/delay_edges.vhd @@ -0,0 +1,28 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity delay_edges is + generic ( + D_RISE: time := 0 ns; + D_FALL: time := 0 ns + ); + port ( + sig_in: in std_logic; + sig_out: out std_logic + ); +end delay_edges; + +architecture behavioral of delay_edges is +begin + + delayed_a: if D_RISE > D_FALL generate + sig_out <= sig_in'delayed(D_RISE) and sig_in'delayed(D_FALL); + end generate; + + delayed_b: if D_RISE <= D_FALL generate + sig_out <= sig_in'delayed(D_RISE) or sig_in'delayed(D_FALL); + end generate; + +end behavioral; + diff --git a/libraries/simulated/is_unstable.vhd b/libraries/simulated/is_unstable.vhd new file mode 100644 index 0000000..b0e0b58 --- /dev/null +++ b/libraries/simulated/is_unstable.vhd @@ -0,0 +1,24 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity is_unstable is + generic ( + T: time := 0 ns; + T_HOLD: time := 0 ns + ); + port ( + sig_in: in std_logic; + sig_out: out std_logic + ); +end is_unstable; + + +architecture behavioral of is_unstable is +begin + + sig_out <= '1' when (sig_in'delayed(T_HOLD) /= sig_in'delayed(T)) or + (sig_in'last_event < T and sig_in'last_event >= T_HOLD) + else '0'; + +end behavioral; diff --git a/libraries/simulated/is_unstable_v.vhd b/libraries/simulated/is_unstable_v.vhd new file mode 100644 index 0000000..3b447da --- /dev/null +++ b/libraries/simulated/is_unstable_v.vhd @@ -0,0 +1,24 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity is_unstable_v is + generic ( + T: time := 0 ns; + T_HOLD: time := 0 ns + ); + port ( + sig_in: in std_logic_vector; + sig_out: out std_logic + ); +end is_unstable_v; + + +architecture behavioral of is_unstable_v is +begin + + sig_out <= '1' when (sig_in'delayed(T_HOLD) /= sig_in'delayed(T)) or + (sig_in'last_event < T and sig_in'last_event >= T_HOLD) + else '0'; + +end behavioral; diff --git a/libraries/simulated/js28f128j3d75.vhd b/libraries/simulated/js28f128j3d75.vhd new file mode 100644 index 0000000..4068dbf --- /dev/null +++ b/libraries/simulated/js28f128j3d75.vhd @@ -0,0 +1,1143 @@ +-------------------------------------------------------------------------------- +-- Notes: +-- +-- In ISim 14.7, it appears that the 'stable attribute doesn't work +-- Checking if 'last_event >= T_x doesn't seem to work with no previous events +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity js28f128j3d75 is + port ( + a: in std_logic_vector(23 downto 0); + d: inout std_logic_vector(15 downto 0); + ce: in std_logic_vector(2 downto 0); + rp_n: in std_logic; + oe_n: in std_logic; + we_n: in std_logic; + sts: out std_logic; -- open drain + byte_n: in std_logic; + vpen: in std_logic + ); +end js28f128j3d75; + + +architecture behavioral of js28f128j3d75 is + + -- Timings for 128Mbit, J3D-75 parts + + -- Read timings + constant T_AVAV: time := 75 ns; -- R1, Read/Write Cycle Time + constant T_AVQV: time := 75 ns; -- R2, Address to Output Delay + constant T_ELQV: time := 75 ns; -- R3, CEx to Output Delay + constant T_GLQV_NA: time := 25 ns; -- R4, OE# to Non-Array Output Delay + constant T_PHQV: time := 210 ns; -- R5, RP# High to Output Delay + constant T_ELQX: time := 0 ns; -- R6, CEx to Output Low Z + constant T_GLQX: time := 0 ns; -- R7, OE# to Output Low Z + constant T_EHQZ: time := 25 ns; -- R8, CEx High to Output in High Z + constant T_GHQZ: time := 15 ns; -- R9, OE# High to Output in High Z + constant T_OH: time := 0 ns; -- R10, Output Hold from Address, CEx, or OE# change + constant T_ELFL: time := 10 ns; -- R11, CEx Low to BYTE# High or Low + constant T_ELFH: time := 10 ns; -- R11, (Same as Above) + constant T_FLQV: time := 1 ns; -- R12, BYTE# to Output Delay + constant T_FHQV: time := 1 ns; -- R12, (Same as Above) + constant T_FLQZ: time := 1 ns; -- R13, BYTE# to Output in High Z + constant T_EHEL: time := 0 ns; -- R14, CEx High to CEx Low + constant T_APA: time := 25 ns; -- R15, Page Address Access Time + constant T_GLQV_A: time := 25 ns; -- R16, OE# to Array Output Delay + + -- Write timings + constant T_PHWL: time := 210 ns; -- W1, RP# High Recovery to WE# (CEx) Going Low + constant T_PHEL: time := 210 ns; -- W1, (Same as Above) + constant T_ELWL: time := 0 ns; -- W2, CEx (WE#) Low to WE# (CEx) Going Low + constant T_WLEL: time := 0 ns; -- W2, (Same as Above) + constant T_WP: time := 60 ns; -- W3, Write Pulse Width + constant T_DVWH: time := 50 ns; -- W4, Data Setup to WE# (CEx) Going High + constant T_DVEH: time := 50 ns; -- W4, (Same as Above) + constant T_AVWH: time := 55 ns; -- W5, Address Setup to WE# (CEx) Going High + constant T_AVEH: time := 55 ns; -- W5, (Same as Above) + constant T_WHEH: time := 0 ns; -- W6, CEx (WE#) Hold from WE# (CEx) High + constant T_EHWH: time := 0 ns; -- W6, (Same as Above) + constant T_WHDX: time := 0 ns; -- W7, Data Hold from WE# (CEx) High + constant T_EHDX: time := 0 ns; -- W7, (Same as Above) + constant T_WHAX: time := 0 ns; -- W8, Address Hold from WE# (CEx) High + constant T_EHAX: time := 0 ns; -- W8, (Same as Above) + constant T_WPH: time := 30 ns; -- W9, Write Pulse Width High + constant T_VPWH: time := 0 ns; -- W11, Vpen Setup to WE# (CEx) Going High + constant T_VPEH: time := 0 ns; -- W11, (Same as Above) + constant T_WHGL: time := 35 ns; -- W12, Write Recovery before Read + constant T_EHGL: time := 35 ns; -- W12, (Same as Above) + constant T_WHRL: time := 500 ns; -- W13, WE# (CEx) High to STS Going Low + constant T_EHRL: time := 500 ns; -- W13, (Same as Above) + constant T_QVVL: time := 0 ns; -- W15, Vpen Hold from Valid SRD, STS Going High + + -- Configuration timings + constant T_WHQV3: time := 175 us; -- W16, Byte Program Time (Using Word/Byte Program Command) + constant T_EHQV3: time := 175 us; -- W16, (Same as above) + constant T_WHQV4: time := 4 sec; -- W16, Block Erase Time + constant T_EHQV4: time := 4 sec; -- W16, (Same as above) + constant T_WHQV5: time := 60 us; -- W16, Set Lock-Bit Time + constant T_EHQV5: time := 60 us; -- W16, (Same as above) + constant T_WHQV6: time := 0.7 sec; -- W16, Clear Block Lock-Bits Time + constant T_EHQV6: time := 0.7 sec; -- W16, (Same as above) + constant T_WHRH1: time := 20 us; -- W16, Program Suspend Latency Time to Read + constant T_EHRH1: time := 20 us; -- W16, (Same as above) + constant T_WHRH: time := 20 us; -- W16, Erase Suspend Latency Time to Read + constant T_EHRH: time := 20 us; -- W16, (Same as above) + constant T_STS: time := 500 ns; -- WY, STS Pulse Width Low Time + + -- Reset timings + constant T_PLPH: time := 25 us; -- P1, RP# Pulse Low Time + constant T_PHRH: time := 100 ns; -- P2, RP# High to Reset During Block-Erase, Program, or Lock-Bit Configuration + constant T_VCCPH: time := 60 us; -- P3, Vcc Power Valid to RP# de-assertion (high) + + pure function block_index_of(address: std_logic_vector(a'high downto 0)) return integer is + begin + return to_integer(unsigned(address(address'high downto 17))); + end function; + + pure function word_index_of(address: std_logic_vector(a'high downto 0)) return integer is + begin + return to_integer(unsigned(address(16 downto 1))); + end function; + + -- Internal types + type word_array is array(natural range <>) of std_logic_vector(15 downto 0); + type block_array is array(natural range<>) of word_array(65535 downto 0); + + type cui_state is ( + C_IDLE, + C_PECR, + C_POTP, + C_STSC, + C_PROG, + C_BUF_PROG_COUNT, + C_BUF_PROG_DATA, + C_BUF_PROG_CONF, + C_ERASE, + C_LOCK + ); + type read_state is (R_ARRAY, R_ID, R_STATUS, R_CFI); + type wsm_state is (W_READY, W_PROG, W_BUF_PROG, W_ERASE, W_LOCK_PROG, W_LOCK_ERASE, W_OTP_PROG, W_RESUME); + + -- Internal signals + signal internal_ce_n: std_logic; -- Composite chip-enable from multiple CE input signals + signal internal_we_n: std_logic; -- Composite write-latch from WE# and CEx + + signal array_pre_word: std_logic_vector(15 downto 0); + signal array_word: std_logic_vector(15 downto 0); + signal cfi_pre_word: std_logic_vector(15 downto 0); + signal cfi_word: std_logic_vector(15 downto 0); + signal id_pre_word: std_logic_vector(15 downto 0); + signal id_word: std_logic_vector(15 downto 0); + signal status_pre_word: std_logic_vector(15 downto 0); + signal status_word: std_logic_vector(15 downto 0); + signal read_pre_word: std_logic_vector(15 downto 0); + signal read_word: std_logic_vector(15 downto 0); + signal last_word: std_logic_vector(15 downto 0); + + -- Delay mask signals for array reads + signal addr_hi_change_4: std_logic; + signal addr_hi_change_8: std_logic; + signal addr_hi_change: std_logic; + signal is_sensing: std_logic; + signal addr_lo_change_4: std_logic; + signal addr_lo_change_8: std_logic; + signal addr_lo_change: std_logic; + signal oe_change_array: std_logic; + + -- Delay mask signals for non-array reads + signal addr_change: std_logic; + signal oe_change_nonarray: std_logic; + + -- Delay mask signals for all reads + signal rp_change: std_logic; + signal ce_change: std_logic; + signal byte_change: std_logic; + + -- Delay mask signals for tristate logic + signal ce_n_gate: std_logic; + signal oe_n_gate: std_logic; + signal final_oe_n: std_logic; + + signal cur_cui_state: cui_state; + signal cur_read_state: read_state; + signal cur_wsm_state: wsm_state; + signal next_wsm_state: wsm_state; + signal wsm_trigger: std_logic; + + -- Status signals + signal sts_ready: std_logic := '1'; + signal sts_susp_erase: std_logic := '0'; + signal sts_error_code: std_logic_vector(1 downto 0) := "00"; + signal sts_error_volt: std_logic := '0'; + signal sts_susp_prog: std_logic := '0'; + signal sts_error_lock: std_logic := '0'; + signal sts_config: std_logic_vector(7 downto 0) := (others => '0'); + + -- Flash array data + -- Only two blocks to save the simulator + signal flash_array: block_array(1 downto 0) := (others => (others => (others => '1'))); + + -- Lock operation state + signal lock_address: std_logic_vector(a'range); + signal lock_reg: std_logic_vector(127 downto 0) := (others => '0'); + + -- Program array operation state + signal prog_count: integer; + signal prog_address: std_logic_vector(a'range); + signal prog_buf: word_array(31 downto 0); + signal prog_word: std_logic_vector(15 downto 0); + + -- Erase operation state + signal erase_address: std_logic_vector(a'range); + + -- Enhanced Configuration Register + signal ecr_reg: std_logic_vector(16 downto 0); + signal ecr_8word_page: std_logic; + + -- OTP Protection Register + signal otp_prog_offset: std_logic_vector(a'range); + signal otp_prog_data: std_logic_vector(15 downto 0); + signal plr_reg: std_logic_vector(15 downto 0) := x"FFFE"; + signal otp_reg: word_array(7 downto 0) := (others => (others => '1')); + + -- Debug + signal ce_stable: boolean; +begin + + ---------------------------------------------------------------------------- + -- Useful internal signals composed from external signals + + -- Decode multiple chip enable inputs into single chip enable signal + with ce select internal_ce_n <= + '0' when "000", + '1' when "001", + '1' when "010", + '1' when "011", + '0' when "100", + '0' when "101", + '0' when "110", + '1' when "111", + 'X' when others; + + -- Write latch is triggered by either WEx or CEx going high, whichever is first + internal_we_n <= we_n or internal_ce_n; + + -- STS signal represents the state of the WSM + sts <= 'H' when sts_ready = '1' else '0'; + + + ---------------------------------------------------------------------------- + -- Reset logic + + reset: process (rp_n) + begin + if rp_n = '0' then + -- reset all state machines + -- cur_cui_state <= C_IDLE; + -- cur_read_state <= R_ARRAY; + -- cur_wsm_state <= W_READY; + + -- reset status register and status config + --sts_ready <= '1'; + --sts_susp_erase <= '0'; + --sts_error_code <= "00"; + --sts_error_volt <= '0'; + --sts_susp_prog <= '0'; + --sts_error_lock <= '0'; + sts_config <= (others => '0'); + + -- reset ECR to 4-word page mode + ecr_reg <= (others => '0'); + + -- abort all program, erase, lock-bit operations + end if; + end process; + + + ---------------------------------------------------------------------------- + -- Read logic + + -- Look up value from flash array + array_pre_word <= flash_array(block_index_of(a))(word_index_of(a)); + + -- Generate mask signal representing sensing delay + -- ce_change is incorporated here because the datasheet says ce falling triggers sensing, + -- but the same delay also listed for all other reads, so it is incorporated again below. + addr_hi_change_4 <= '1' when (a(a'high downto 3) /= a(a'high downto 3)'delayed(T_AVQV)) or + (a(a'high downto 3)'last_event < T_AVQV) + else '0'; + addr_hi_change_8 <= '1' when (a(a'high downto 4) /= a(a'high downto 4)'delayed(T_AVQV)) or + (a(a'high downto 4)'last_event < T_AVQV) + else '0'; + with ecr_8word_page select addr_hi_change <= + addr_hi_change_8 when '1', + addr_hi_change_4 when others; + ce_change <= '1' when (internal_ce_n /= internal_ce_n'delayed(T_ELQV)) or + (internal_ce_n'last_event < T_ELQV) + else '0'; + is_sensing <= addr_hi_change or ce_change; + + -- Generate mask signal representing word select delay + addr_lo_change_4 <= '1' when (a(2 downto 0) /= a(2 downto 0)'delayed(T_APA)) or + (a(2 downto 0)'last_event < T_APA) + else '0'; + addr_lo_change_8 <= '1' when (a(3 downto 0) /= a(3 downto 0)'delayed(T_APA)) or + (a(3 downto 0)'last_event < T_APA) + else '0'; + with ecr_8word_page select addr_lo_change <= + addr_lo_change_8 when '1', + addr_lo_change_4 when others; + + -- Generate mask signal representing OE# delay for array reads + oe_change_array <= '1' when (oe_n /= oe_n'delayed(T_GLQV_A)) or + (oe_n'last_event < T_GLQV_A) + else '0'; + + array_word <= (others => 'X') when is_sensing = '1' or + addr_lo_change = '1' or + oe_change_array = '1' else array_pre_word; + + + -- Generate mask signals representing delays for non-array reads (and use ce_change from above) + -- Not sure if T_AVAV applies to non-array reads, so apply it anyway just in case + addr_change <= '1' when (a /= a'delayed(T_AVQV)) or + (a'last_event < T_AVQV) + else '0'; + oe_change_nonarray <= '1' when (oe_n /= oe_n'delayed(T_GLQV_NA)) or + (oe_n'last_event < T_GLQV_NA) + else '0'; + + -- Look up CFI query value + do_cfi_read: process (a) + begin + case a(8 downto 1) is + when x"10" => cfi_pre_word <= x"0051"; + when x"11" => cfi_pre_word <= x"0052"; + when x"12" => cfi_pre_word <= x"0059"; + when others => cfi_pre_word <= (others => '0'); + end case; + end process; + + cfi_word <= (others => 'X') when addr_change = '1' or + oe_change_nonarray = '1' else cfi_pre_word; + + -- Look up ID register value + do_id_read: process (a) + begin + case to_integer(unsigned(a(16 downto 1))) is + -- Device ID + when 16#0001# => + id_pre_word <= x"0018"; + + -- Lock bit for this block + when 16#0002# => + id_pre_word <= (others => '0'); + id_pre_word(0) <= lock_reg(to_integer(unsigned(a(a'high downto 17)))); + + -- OTP protection lock register + when 16#0080# => id_pre_word <= plr_reg; + + -- OTP register + when 16#0081# to 16#88# => + id_pre_word <= otp_reg(to_integer(unsigned(a)) - 16#81#); + + when others => id_pre_word <= (others => 'X'); + end case; + end process; + + id_word <= (others => 'X') when addr_change = '1' or + oe_change_nonarray = '1' else id_pre_word; + + -- Compute status register value + do_status: process (internal_ce_n, oe_n, + sts_ready, sts_susp_erase, sts_error_code, sts_error_volt, sts_susp_prog, sts_error_lock) + begin + -- Status register value is latched on CEx or OE# going low + if falling_edge(internal_ce_n) or (falling_edge(oe_n) and internal_ce_n = '0') then + status_pre_word(15 downto 8) <= x"00"; + status_pre_word(7) <= sts_ready; + status_pre_word(6) <= sts_susp_erase; + status_pre_word(5 downto 4) <= sts_error_code; + status_pre_word(3) <= sts_error_volt; + status_pre_word(2) <= sts_susp_prog; + status_pre_word(1) <= sts_error_lock; + status_pre_word(0) <= '0'; + end if; + end process; + + status_word <= (others => 'X') when addr_change = '1' or + oe_change_nonarray = '1' else status_pre_word; + + + -- Choose a value based on read mode + do_read: process (cur_read_state, array_word, cfi_word, id_word, status_word) + begin + case cur_read_state is + when R_ARRAY => + read_pre_word <= array_word; + when R_ID => + read_pre_word <= id_word; + when R_STATUS => + read_pre_word <= status_word; + when R_CFI => + read_pre_word <= cfi_word; + when others => + read_pre_word <= (others => 'X'); + end case; + end process; + + -- Generate mask signals for delays that apply to any read + rp_change <= '1' when (rp_n /= rp_n'delayed(T_PHQV)) or + (rp_n'last_event < T_PHQV) + else '0'; + byte_change <= '1' when (byte_n /= byte_n'delayed(T_FLQV)) or + (byte_n'last_event < T_FLQV) + else '0'; + -- ce_change from above + + -- Handle byte/word modes + byte_select: process (read_pre_word, a(0), byte_n) + begin + if byte_n = '0' then + -- Oops, extra tristating to handle here instead of in tristate logic + -- Good thing is is sim-only! + read_word(15 downto 8) <= (others => 'Z'); + if a(0) = '0' then + read_word(7 downto 0) <= read_pre_word(7 downto 0); + else + read_word(7 downto 0) <= read_pre_word(15 downto 8); + end if; + else + read_word <= read_pre_word; + end if; + end process; + + -- Equal parts gin, green chartreuse, maraschino liqueur, lime; shaken and served up + last_word <= (others => 'X') when rp_change = '1' or + byte_change = '1' else read_word; + + + -- Handle delays between CEx/OE# and transitions between low/high-Z output + -- The front and back porch have different delay specs, so to get the front porch + -- with one delay and the back porch with the other we may need to either and or + -- or the delayed signals depending on which delay is longer. + delayed_ce_n_a: if T_ELQX > T_EHQZ generate + ce_n_gate <= internal_ce_n'delayed(T_ELQX) or internal_ce_n'delayed(T_EHQZ); + end generate; + delayed_ce_n_b: if T_ELQX <= T_EHQZ generate + ce_n_gate <= internal_ce_n'delayed(T_ELQX) and internal_ce_n'delayed(T_EHQZ); + end generate; + delayed_oe_n_a: if T_GLQX > T_GHQZ generate + oe_n_gate <= oe_n'delayed(T_GLQX) or oe_n'delayed(T_GHQZ); + end generate; + delayed_oe_n_b: if T_GLQX <= T_GHQZ generate + oe_n_gate <= oe_n'delayed(T_GLQX) and oe_n'delayed(T_GHQZ); + end generate; + final_oe_n <= ce_n_gate or oe_n_gate; + d <= (others => 'Z') when rp_n = '0' or final_oe_n = '1' else last_word'delayed(T_OH); + + + ecr_8word_page <= ecr_reg(13); + + + ---------------------------------------------------------------------------- + -- Write logic + + -- Latch CUI on rising edge of we_n or ce_n + -- CUI logic is responsible for: + -- * checking sts_ready before triggering + -- * checking sts_susp_prog/erase before triggering if relevant + -- * setting sts_susp_prog/erase when suspending + -- * setting next_wsm_state and wsm_trigger to begin operations + cui: process (internal_we_n, a, d, rp_n, + sts_ready, sts_susp_prog, sts_susp_erase, cur_wsm_state) + variable prog_i: integer; + begin + if rp_n = '0' then + cur_cui_state <= C_IDLE; + cur_read_state <= R_ARRAY; + wsm_trigger <= '0'; + elsif rising_edge(internal_we_n) then + report "PENIS"; + case cur_cui_state is + when C_IDLE => + wsm_trigger <= '0'; + + -- Idle, not waiting for data or confirmation commands + case d(7 downto 0) is + -- Program Enhanced Configuration Register (Setup) + --when x"60" => + -- cur_cui_state <= C_PECR; + + -- Program OTP Register (Setup) + when x"c0" => + cur_cui_state <= C_POTP; + + -- Clear Status Register + when x"50" => + -- Only clear error flags, not status + sts_error_code <= "00"; + sts_error_volt <= '0'; + sts_error_lock <= '0'; + cur_read_state <= R_STATUS; + + -- Program STS Configuration Register (Setup) + when x"b8" => + -- FIXME: enter R_STATUS? + cur_cui_state <= C_STSC; + + -- Read Array + when x"ff" => + assert sts_ready = '1' + report "Entering read-array mode while WSM is busy" + severity warning; + cur_read_state <= R_ARRAY; + + -- Read Status Register + when x"70" => + cur_read_state <= R_STATUS; + + -- Read Identifier Codes + when x"90" => + cur_read_state <= R_ID; + + -- CFI Query + when x"98" => + cur_read_state <= R_CFI; + + -- Word/Byte Program (Setup) + when x"40" | x"10" => + cur_cui_state <= C_PROG; + + -- Buffered Program (Setup) + when x"e8" => + cur_read_state <= R_STATUS; + if sts_ready = '1' then + cur_cui_state <= C_BUF_PROG_COUNT; + end if; + + -- BLock Erase (Setup) + when x"20" => + cur_cui_state <= C_ERASE; + + -- Program/Erase Suspend + when x"b0" => + if sts_ready = '1' then + -- No-op + null; + elsif cur_wsm_state = W_PROG or cur_wsm_state = W_BUF_PROG then + -- Suspend program operation + sts_susp_prog <= '1'; + elsif cur_wsm_state = W_ERASE then + -- Suspend erase operation + sts_susp_erase <= '1'; + end if; + -- Suspend does not change read mode + + -- Program/Erase Resume + when x"d0" => + next_wsm_state <= W_RESUME; + wsm_trigger <= '1'; + cur_read_state <= R_STATUS; + + -- Lock/Unlock Block (Setup) and Program Enhanced Configuration Register (Setup) + when x"60" => + cur_read_state <= R_STATUS; + cur_cui_state <= C_LOCK; + + when others => + sts_error_code <= "11"; + end case; + + -- Started Program Enhanced Configuration Register command + when C_PECR => + case d(7 downto 0) is + when x"04" => + ecr_reg <= a(ecr_reg'high downto 0); + -- Datasheet says it returns to read-array mode... + -- FIXME: Does it just not change the mode? + cur_read_state <= R_ARRAY; + + when others => + sts_error_code <= "11"; + end case; + cur_cui_state <= C_IDLE; + + -- Started Program OTP Register command + when C_POTP => + if sts_ready = '0' then + -- Not allowed while WSM is busy + sts_error_code <= "11"; + elsif sts_susp_prog = '1' or sts_susp_erase = '1' then + -- Not allowed while program/erase suspended + sts_error_code <= "11"; + else + -- Latch address and data and trigger WSM + otp_prog_offset <= a; + otp_prog_data <= d; + next_wsm_state <= W_OTP_PROG; + wsm_trigger <= '1'; + end if; + + cur_read_state <= R_STATUS; + cur_cui_state <= C_IDLE; + + -- Started Program STS Configuration Register command + when C_STSC => + if sts_ready = '0' then + -- Not allowed while WSM is busy + sts_error_code <= "11"; + elsif sts_susp_prog = '1' or sts_susp_erase = '1' then + -- Not allowed while program/erase suspended + sts_error_code <= "11"; + elsif (d and x"00fc") /= x"0000" then + -- Invalid configuration value + sts_error_code <= "11"; + else + assert d(1) = '0' and d(0) = '0' + report "TODO: STS pulse config" + severity warning; + sts_config <= d(7 downto 0); + end if; + cur_read_state <= R_STATUS; + cur_cui_state <= C_IDLE; + + -- Started Word/Byte Program command + when C_PROG => + if sts_ready = '0' then + -- Not allowed while WSM is busy + sts_error_code <= "11"; + elsif sts_susp_prog = '1' then + -- Not allowed while program suspended + sts_error_code <= "11"; + else + -- Latch address and data and trigger WSM + prog_address <= a; + prog_word <= d; + next_wsm_state <= W_PROG; + wsm_trigger <= '1'; + end if; + cur_read_state <= R_STATUS; + cur_cui_state <= C_IDLE; + + -- Started Buffer Program command + when C_BUF_PROG_COUNT => + if sts_ready = '0' then + -- sts_ready already checked in previous state, but just in case + sts_error_code <= "11"; + cur_cui_state <= C_IDLE; + elsif sts_susp_prog = '1' then + -- Do not allow while programming operation is suspended + -- Should this check have been rolled into the status check in + -- the previous state? Or should this check be done in the previous + -- state? TODO: check on hardware + sts_error_code <= "11"; + cur_cui_state <= C_IDLE; + elsif (d(7 downto 0) and x"e0") /= x"0000" then + -- Word count must be <= 31 + report "Buffer program sent with word count greater than 31" severity warning; + sts_error_code <= "11"; + cur_cui_state <= C_IDLE; + else + -- Latch start address and word count + prog_address <= a; + prog_count <= to_integer(unsigned(d)); + prog_i := to_integer(unsigned(d)); + cur_cui_state <= C_BUF_PROG_DATA; + end if; + + when C_BUF_PROG_DATA => + -- Latch data into buffer at address + prog_buf(to_integer(unsigned(a(5 downto 1)))) <= d; + if prog_i = 0 then + cur_cui_state <= C_BUF_PROG_CONF; + else + prog_i := prog_i - 1; + end if; + + when C_BUF_PROG_CONF => + if d(7 downto 0) /= x"d0" then + -- Failed to confirm write, abort + sts_error_code <= "11"; + cur_cui_state <= C_IDLE; + else + -- Trigger WSM + next_wsm_state <= W_BUF_PROG; + wsm_trigger <= '1'; + cur_cui_state <= C_IDLE; + end if; + + -- Started Block Erase command + when C_ERASE => + -- TODO: not allowed when erase or program suspended + if sts_ready = '0' then + -- Not allowed while WSM is busy + sts_error_code <= "11"; + cur_cui_state <= C_IDLE; + elsif sts_susp_prog = '1' or sts_susp_erase = '1' then + -- Not allowed while program/erase suspended + sts_error_code <= "11"; + cur_cui_state <= C_IDLE; + elsif d(7 downto 0) /= x"d0" then + -- Failed to confirm erase + sts_error_code <= "11"; + cur_cui_state <= C_IDLE; + else + -- Latch block address and trigger WSM + erase_address <= a; + next_wsm_state <= W_ERASE; + wsm_trigger <= '1'; + cur_cui_state <= C_IDLE; + end if; + + -- Started Lock/Unlock Block command + when C_LOCK => + -- Read state already set to R_STATUS in previous cycle + case d(7 downto 0) is + -- Program Enhanced Configuration Register + when x"04" => + ecr_reg <= a(ecr_reg'high downto 0); + -- Datasheet says it returns to read-array mode + cur_read_state <= R_ARRAY; + + -- Lock Block + when x"01" => + -- address in a + if sts_ready = '0' then + -- Cannot program when WSM is busy + sts_error_code <= "11"; + elsif sts_susp_erase = '1' or sts_susp_prog = '1' then + -- Cannot program when erase or program suspended + sts_error_code <= "11"; + else + lock_address <= a; + next_wsm_state <= W_LOCK_PROG; + wsm_trigger <= '1'; + end if; + + -- Unlock Blocks + when x"d0" => + if sts_ready = '0' then + -- Cannot erase when WSM is busy + sts_error_code <= "11"; + elsif sts_susp_erase = '1' or sts_susp_prog = '1' then + -- Cannot erase when erase or program suspended + sts_error_code <= "11"; + else + next_wsm_state <= W_LOCK_ERASE; + wsm_trigger <= '1'; + end if; + + when others => + sts_error_code <= "11"; + end case; + cur_cui_state <= C_IDLE; + + when others => + cur_cui_state <= C_IDLE; + end case; + end if; + end process; + + -- Write state machine is responsible for checking: + -- * Vpen state + -- * valid offset/address + -- * lock protection + -- * checking wsm_trigger and next_wsm_state to begin operations + -- * checking sts_susp_* state to suspend operations + wsm: process (rp_n, vpen, sts_susp_erase, sts_susp_prog, wsm_trigger, cur_wsm_state, next_wsm_state) + variable otp_prog_idx: integer; + variable prog_buf_addr: integer; + begin + if rp_n = '0' then + -- TODO: hold ready low for some time after reset + cur_wsm_state <= W_READY; + sts_ready <= '1'; + sts_susp_erase <= '0'; + sts_error_code <= "00"; + sts_error_volt <= '0'; + sts_susp_prog <= '0'; + sts_error_lock <= '0'; + end if; + + sts_ready <= '0'; -- Default to not-ready + + -- Process triggers from the CUI FSM + if rising_edge(wsm_trigger) then + cur_wsm_state <= next_wsm_state; + end if; + + case cur_wsm_state is + when W_READY => + sts_ready <= '1'; -- Ready status only in ready state + + when W_PROG => + -- Abort with voltage error if Vpen drops + sts_ready <= '0'; + if vpen = '0' then + sts_error_code(0) <= '1'; + sts_error_volt <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- Suspend programming operation when signaled + if sts_susp_prog = '1' then + cur_wsm_state <= W_READY after T_WHRH1; -- T_EHRH1 + end if; + -- Check lock protection bit for block + if lock_reg(block_index_of(prog_address)) = '1' then + sts_error_code(0) <= '1'; + sts_error_lock <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + flash_array(block_index_of(prog_address))(word_index_of(prog_address)) <= + flash_array(block_index_of(prog_address))(word_index_of(prog_address)) + and prog_word; + cur_wsm_state <= W_READY after T_WHQV3; -- T_EHQV3 + + when W_BUF_PROG => + -- Abort with voltage error if Vpen drops + if vpen = '0' then + sts_error_code(0) <= '1'; + sts_error_volt <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- Suspend programming operation when signaled + if sts_susp_prog = '1' then + cur_wsm_state <= W_READY after T_WHRH1; -- T_EHRH1 + end if; + -- Check lock protection bit for block + if lock_reg(block_index_of(prog_address)) = '1' then + sts_error_code(0) <= '1'; + sts_error_lock <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- TODO: write zeroes + prog_buf_addr := to_integer(unsigned(prog_address)); + for addr in prog_buf_addr to prog_buf_addr + prog_count + 1 loop + flash_array(block_index_of(std_logic_vector(to_unsigned(addr, 24))))(word_index_of(std_logic_vector(to_unsigned(addr, 24)))) <= + flash_array(block_index_of(std_logic_vector(to_unsigned(addr, 24))))(word_index_of(std_logic_vector(to_unsigned(addr, 24)))) + and prog_buf(addr mod 32); + end loop; + -- TODO: get correct timing + cur_wsm_state <= W_READY after T_WHQV3; -- T_EHQV3 + + when W_ERASE => + -- Abort with voltage error if Vpen drops + if vpen = '0' then + sts_error_code(1) <= '1'; + sts_error_volt <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- Suspend erase operation when signaled + if sts_susp_erase = '1' then + cur_wsm_state <= W_READY after T_WHRH; -- T_EHRH + end if; + -- Check lock protection bit for block + if lock_reg(block_index_of(erase_address)) = '1' then + sts_error_code(1) <= '1'; + sts_error_lock <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- Erase block and return to ready + flash_array(block_index_of(erase_address)) <= (others => (others => '1')); + cur_wsm_state <= W_READY after T_WHQV4; -- T_EHQV4 + + when W_LOCK_PROG => + -- Abort with voltage error if Vpen drops + if vpen = '0' then + sts_error_code(0) <= '1'; + sts_error_volt <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- Write lock bit and return to ready + lock_reg(block_index_of(lock_address)) <= '1'; + cur_wsm_state <= W_READY after T_WHQV5; -- T_EHQV5 + + when W_LOCK_ERASE => + -- Abort with voltage error if Vpen drops + if vpen = '0' then + sts_error_code(1) <= '1'; + sts_error_volt <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- Erase all lock bits and return to ready + lock_reg <= (others => '0'); + cur_wsm_state <= W_READY after T_WHQV6; -- T_EHQV6 + + when W_OTP_PROG => + -- Abort with voltage error if Vpen drops + if vpen = '0' then + sts_error_code(0) <= '1'; + sts_error_volt <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- Check for out-of-range address + otp_prog_idx := to_integer(unsigned(otp_prog_offset(otp_prog_offset'high downto 1))); + if otp_prog_idx < 16#80# or otp_prog_idx > 16#88# then + sts_error_code(0) <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + -- Check for protection bits + if otp_prog_idx >= 16#81# and otp_prog_idx <= 16#84# then + if plr_reg(0) = '0' then + sts_error_code(0) <= '1'; + sts_error_lock <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + end if; + if otp_prog_idx >= 16#85# and otp_prog_idx <= 16#88# then + if plr_reg(1) = '0' then + sts_error_code(0) <= '1'; + sts_error_lock <= '1'; + cur_wsm_state <= W_READY after T_STS; + end if; + end if; + -- Write zeroes and return to ready + if otp_prog_idx = 16#80# then + plr_reg <= plr_reg and otp_prog_data; + else + otp_prog_idx := otp_prog_idx - 16#81#; + otp_reg(otp_prog_idx) <= otp_reg(otp_prog_idx) and otp_prog_data; + end if; + cur_wsm_state <= W_READY after T_WHQV5; -- Best guess for timing + + when W_RESUME => + if sts_susp_prog = '1' then + -- Precedence goes to resuimg programming operation + sts_susp_prog <= '0'; + cur_wsm_state <= W_PROG after T_STS; + elsif sts_susp_erase = '1' then + -- Resume erase operation if no programming operation pending + sts_susp_erase <= '0'; + cur_wsm_state <= W_ERASE after T_STS; + else + -- Nothing to resume, return to ready + cur_wsm_state <= W_READY after T_STS; + end if; + + when others => + cur_wsm_state <= W_READY after T_STS; + end case; + end process; + + + ---------------------------------------------------------------------------- + -- Check for erroneous usages + + -- Should not try to read and write at the same time + process (oe_n, we_n) + begin + assert not (oe_n = '0' and we_n = '0') + report "OE# and WE# should never be enabled simultaneously." + severity error; + end process; + + -- Address must be valid for a full read or write cycle + R1: process (oe_n, a(23 downto 4)) + begin + -- FIXME: Check more address bits if in non-array or 4-word array modes + if a(23 downto 4)'event and oe_n = '0' then + assert a(23 downto 4)'last_event >= T_AVAV; + report "T_AVAV not met (Read/Write Cycle Time)" + severity error; + end if; + end process; + + -- BYTE# cannot change more than T_ELFL/T_ELFH after CEx goes low + R11: process (byte_n, internal_ce_n) + begin + if byte_n'event and internal_ce_n = '0' then + assert internal_ce_n'last_event <= T_ELFL; + report "T_ELFL/H not met (CEx Low to BYTE# High or Low)" + severity error; + end if; + end process; + + -- WE# and CEx cannot become active less than T_PHWL/EL after RP# goes high + W1: process (rp_n, we_n, internal_ce_n) + begin + if rp_n = '1' then + if falling_edge(we_n) then + assert rp_n'last_event >= T_PHWL + report "T_PHWL not met (RP# High Recovery to WE# Going Low)" + severity error; + end if; + if falling_edge(internal_ce_n) then + assert rp_n'last_event >= T_PHEL + report "T_PHEL not met (RP# High Recovery to CEx Going Low)" + severity error; + end if; + end if; + end process; + + -- CEx and WE# may have set-up timing constraints relative to each other + W2: process (we_n, internal_ce_n) + begin + if falling_edge(we_n) and internal_ce_n = '0' then + assert internal_ce_n'last_event >= T_ELWL + report "T_ELWL not met (CEx Low to WE# Going Low)" + severity error; + end if; + if falling_edge(internal_ce_n) and we_n = '0' then + assert we_n'last_event >= T_WLEL + report "T_WLEL not met (WE# Low to CEx Going Low)" + severity error; + end if; + end process; + + -- WE# has a minimum low pulse width + W3: process (we_n) + begin + if rising_edge(we_n) then + --assert we_n'last_event >= T_WP + assert we_n'delayed(T_WP) = '0' + report "T_WP not met (Write Pulse Width)" + severity error; + end if; + end process; + + -- Data-in set-up time (no need to use internal_we_n here) + W4: process (internal_we_n, d) + begin + if rising_edge(internal_we_n) then + -- T_DVEH and T_DVWH are the same in the datasheet, so they probably + -- refer to the internal write-latch signal composed of CEx and WE#. + -- Checking against the internal signal prevents us from checking + -- timing between our own output and CEx during write cycles. + assert d'delayed'last_event >= T_DVWH -- Also T_DVEH + report "T_DVWH/T_DVEH not met (Data Setup to WE#/CEx Going High)" & time'image(d'last_event) + severity error; + end if; + --if rising_edge(we_n) then + -- assert d'last_event >= T_DVWH + -- report "T_DVWH not met (Data Setup to WE# Going High)" + -- severity error; + --end if; + --if rising_edge(internal_ce_n) then + -- -- Have seen some false-positives where d'last_event = 0 ps, but d'event = False + -- -- Delaying 1 ps will get past any event in the current simulation cycle + -- assert d'delayed(1 ps)'last_event >= T_DVEH + -- report "T_DVEH not met (Data Setup to CEx Going High)" + -- severity error; + --end if; + end process; + + -- Address set-up time (no need to use internal_we_n here) + W5: process (internal_we_n, a) + begin + if rising_edge(internal_we_n) then + -- T_AVEH and T_AVWH are the same in the datasheet, so they probably + -- refer to the internal write-latch signal composed of CEx and WE#. + -- Checking against the internal signal prevents us from checking + -- timing against CEx during read cycles. + assert a'last_event >= T_AVWH -- Also T_DVEH + report "T_AVWH/T_DVEH not met (Data Setup to WE#/CEx Going High)" + severity error; + end if; + --if rising_edge(we_n) then + -- assert a'last_event >= T_AVWH + -- report "T_AVWH not met (Address Setup to WE# Going High)" + -- severity error; + --end if; + --if rising_edge(internal_ce_n) then + -- assert a'last_event >= T_AVEH + -- report "T_AVEH not met (Address Setup to CEx Going High)" + -- severity error; + --end if; + end process; + + -- CEx and WE# may have hold timing constraints relative to each other + W6: process (we_n, internal_ce_n) + begin + if rising_edge(we_n) and internal_ce_n = '1' then + assert internal_ce_n'last_event >= T_EHWH + report "T_EHWH not met (WE# Hold from CEx High)" + severity error; + end if; + if rising_edge(internal_ce_n) and we_n = '1' then + assert we_n'last_event >= T_WHEH + report "T_WHEH not met (CEx Hold from WE# High)" + severity error; + end if; + end process; + + -- Data-in hold time + W7: process (internal_we_n, d) + begin + if d'event and internal_we_n = '1' then + assert internal_we_n'last_event >= T_WHDX -- T_EHDX + report "T_W/EHDX not met (Data Hold from WE# (CEx) High)" + severity error; + end if; + end process; + + -- Address hold time + W8: process (internal_we_n, a) + begin + if a'event and internal_we_n = '1' then + assert internal_we_n'last_event >= T_WHAX -- T_EHAX + report "T_W/EHAX not met (Address Hold from WE# (CEx) High)" + severity error; + end if; + end process; + + -- WE# has a minimum high pulse width + W9: process (we_n) + begin + if falling_edge(we_n) then + --assert we_n'last_event >= T_WPH + assert we_n'delayed(T_WPH) = '1' + report "T_WPH not met (Write Pulse Width High)" + severity error; + end if; + end process; + + -- Vpen set-up time before write latch (don't need to use internal_we_n here) + W11: process (we_n, internal_ce_n, vpen) + begin + if rising_edge(we_n) and vpen = '1' then + assert vpen'last_event >= T_VPWH + report "T_VPWH not met (Vpen Setup to WE# High)" + severity error; + end if; + if rising_edge(internal_ce_n) and vpen = '1' then + assert vpen'last_event >= T_VPEH + report "T_VPEH not met (Vpen Setup to CEx High)" + severity error; + end if; + end process; + + -- Write recovery before Read + W12: process (internal_we_n, oe_n) + begin + if falling_edge(oe_n) and internal_we_n = '1' then + assert internal_we_n'last_event >= T_WHGL -- T_EHGL + report "T_W/EHGL not met (Write Recovery before Read)" + severity error; + end if; + end process; + + -- Vpen hold time + W15: process (vpen) + begin + -- TODO: T_QVVL never shown on any waveform diagrams + end process; + + -- RP# pulse time + P1: process (rp_n) + begin + if rising_edge(rp_n) then + -- If the simulations tarts with rp_n = '0' then last_event won't work + -- assert rp_n'last_event >= T_PLPH + assert rp_n'delayed(T_PLPH) = '0' + report "T_PLPH not met (RP# Pulse Low Time)" + severity error; + end if; + end process; + +end behavioral; diff --git a/libraries/simulated/mt45w8mw16bgx.vhd b/libraries/simulated/mt45w8mw16bgx.vhd new file mode 100644 index 0000000..1d88889 --- /dev/null +++ b/libraries/simulated/mt45w8mw16bgx.vhd @@ -0,0 +1,333 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library simulated; + + +entity mt45w8mw16bgx is + port ( + a: in std_logic_vector(22 downto 0); + dq: inout std_logic_vector(15 downto 0); + clk: in std_logic; + adv_n: in std_logic; + cre: in std_logic; + ce_n: in std_logic; + oe_n: in std_logic; + we_n: in std_logic; + lb_n: in std_logic; + ub_n: in std_logic; + rwait: out std_logic + ); +end mt45w8mw16bgx; + + +architecture behavioral of mt45w8mw16bgx is + + -- Timings (-708/80MHz) + constant T_AA: time := 70 ns; -- Address access time + constant T_AADV: time := 70 ns; -- ADV# access time + constant T_ABA: time := 46.5 ns; -- + constant T_ACLK: time := 9 ns; -- + constant T_APA: time := 20 ns; -- Page access time + constant T_AS: time := 0 ns; -- + constant T_AVH: time := 2 ns; -- Address hold from ADV# HIGH + constant T_AVS: time := 5 ns; -- Address setup to ADV# HIGH + constant T_AW: time := 70 ns; -- + constant T_BA: time := 70 ns; -- LB#/UB# access time + constant T_BHZ: time := 8 ns; -- LB#/UB# disable to DQ High-Z output + constant T_BLZ: time := 10 ns; -- LB#/UB# enable to Low-Z output + constant T_BOE: time := 20 ns; -- + constant T_BW: time := 70 ns; -- + constant T_CBPH: time := 6 ns; -- + constant T_CEM: time := 4 us; -- Maximum CE# pulse width + constant T_CEW: time := 7.5 ns; -- CE# LOW to WAIT valid + constant T_CLK: time := 12.5 ns; -- + constant T_CO: time := 70 ns; -- Chip select access time + constant T_CPH: time := 5 ns; -- + constant T_CSP: time := 4 ns; -- + constant T_CVS: time := 7 ns; -- CE# LOW to ADV# HIGH + constant T_CW: time := 70 ns; -- + constant T_DH: time := 0 ns; -- + constant T_DPD: time := 10 us; -- + constant T_DPDX: time := 10 us; -- + constant T_DW: time := 20 ns; -- + constant T_HD: time := 2 ns; -- + constant T_HZ: time := 8 ns; -- Chip disable to DQ and WAIT High-Z output + constant T_KHKL: time := 1.8 ns; -- + constant T_KHTL: time := 9 ns; -- + constant T_KOH: time := 2 ns; -- + constant T_KP: time := 4 ns; -- + constant T_LZ: time := 10 ns; -- Chip enable to Low-Z output + constant T_OE: time := 20 ns; -- Output enable to valid output + constant T_OH: time := 5 ns; -- Output hold from address change + constant T_OHZ: time := 8 ns; -- Output disable to DQ High-Z output + constant T_OLZ: time := 3 ns; -- Output enable to Low-Z output + constant T_OW: time := 5 ns; -- + constant T_PC: time := 20 ns; -- Page READ cycle time + constant T_PU: time := 150 us; -- + constant T_RC: time := 70 ns; -- READ cycle time + constant T_SP: time := 3 ns; -- + constant T_VP: time := 5 ns; -- ADV# pulse width LOW + constant T_VS: time := 70 ns; -- + constant T_WC: time := 70 ns; -- + constant T_WHZ: time := 8 ns; -- + constant T_WP: time := 45 ns; -- + constant T_WPH: time := 10 ns; -- + constant T_WR: time := 0 ns; -- + + -- Internal types + type word_array is array(natural range <>) of std_logic_vector(15 downto 0); + + -- Data array read signals + signal array_reg: word_array(15 downto 0) := (others => (others => '1')); + signal array_word: std_logic_vector(15 downto 0); + signal read_word: std_logic_vector(15 downto 0); + signal xmask_addr: std_logic; + signal xmask_ce_n: std_logic; + signal xmask_oe_n: std_logic; + signal xmask_ub_n: std_logic; + signal xmask_lb_n: std_logic; + signal xmask_hi: std_logic; + signal xmask_lo: std_logic; + signal zmask_ce_n: std_logic; + signal zmask_oe_n: std_logic; + signal zmask_ub_n: std_logic; + signal zmask_lb_n: std_logic; + signal zmask_hi: std_logic; + signal zmask_lo: std_logic; + + -- Data array write signals + signal internal_we_ub_n: std_logic; + signal internal_we_lb_n: std_logic; + + -- Configuration registers + signal bcr_reg: std_logic_vector(15 downto 0) := x"9d1f"; + signal rcr_reg: std_logic_vector(15 downto 0) := x"0010"; + signal didr_reg: std_logic_vector(15 downto 0) := x"0343"; + +begin + + ---------------------------------------------------------------------------- + -- Asynchronous array reads + + -- Look up value from memory array, delay to allow for hold time + array_word <= array_reg(to_integer(unsigned(a))) after T_HZ; -- T_OHZ, T_BHZ + + -- Generate mask for periods of time with invalid data + -- FIXME: this may break hold time + xm_addr: entity simulated.is_unstable_v generic map (T => T_AA, T_HOLD => T_OH) + port map (sig_in => a, sig_out => xmask_addr); + xm_ce: entity simulated.is_unstable generic map (T => T_CO, T_HOLD => T_HZ) + port map (sig_in => ce_n, sig_out => xmask_ce_n); + xm_oe: entity simulated.is_unstable generic map (T => T_OE, T_HOLD => T_OHZ) + port map (sig_in => oe_n, sig_out => xmask_oe_n); + xm_ub: entity simulated.is_unstable generic map (T => T_BA, T_HOLD => T_BHZ) + port map (sig_in => ub_n, sig_out => xmask_ub_n); + xm_lb: entity simulated.is_unstable generic map (T => T_BA, T_HOLD => T_BHZ) + port map (sig_in => lb_n, sig_out => xmask_lb_n); + xmask_hi <= xmask_addr or xmask_ce_n or xmask_oe_n or xmask_ub_n; + xmask_lo <= xmask_addr or xmask_ce_n or xmask_oe_n or xmask_lb_n; + + -- Invalidate data for some periods of time + read_word(15 downto 8) <= (others => 'X') when xmask_hi = '1' else array_word(15 downto 8); + read_word( 7 downto 0) <= (others => 'X') when xmask_lo = '1' else array_word( 7 downto 0); + + -- Generate mask for periods of time where data bus is high-z + zm_ce: entity simulated.delay_edges generic map (D_RISE => T_HZ, D_FALL => T_LZ) + port map (sig_in => ce_n, sig_out => zmask_ce_n); + zm_oe: entity simulated.delay_edges generic map (D_RISE => T_OHZ, D_FALL => T_OLZ) + port map (sig_in => oe_n, sig_out => zmask_oe_n); + zm_ub: entity simulated.delay_edges generic map (D_RISE => T_BHZ, D_FALL => T_BLZ) + port map (sig_in => ub_n, sig_out => zmask_ub_n); + zm_lb: entity simulated.delay_edges generic map (D_RISE => T_BHZ, D_FALL => T_BLZ) + port map (sig_in => lb_n, sig_out => zmask_lb_n); + zmask_hi <= zmask_ce_n or zmask_oe_n or zmask_ub_n; + zmask_lo <= zmask_ce_n or zmask_oe_n or zmask_lb_n; + + -- Generate output data signals + dq(15 downto 8) <= (others => 'Z') when zmask_hi = '1' or we_n = '0' + else read_word(15 downto 8); + dq( 7 downto 0) <= (others => 'Z') when zmask_lo = '1' or we_n = '0' + else read_word( 7 downto 0); + + + ---------------------------------------------------------------------------- + -- Asynchronous array writes + + -- Internal signals to trigger latching of data + internal_we_ub_n <= ce_n or we_n or ub_n; + internal_we_lb_n <= ce_n or we_n or lb_n; + + -- Latch data on rising edge of write signal + process (internal_we_ub_n, internal_we_lb_n) + begin + -- Changing (or tristating) DQ at the same moment as the write rising-edge should + -- be allowed (T_DH, data hold time, = 0 ns), but this breaks in ISim 14.7, hence + -- the 1 ps delay. + if rising_edge(internal_we_ub_n) then + array_reg(to_integer(unsigned(a)))(15 downto 8) <= dq(15 downto 8)'delayed(1 ps); + end if; + + if rising_edge(internal_we_lb_n) then + array_reg(to_integer(unsigned(a)))( 7 downto 0) <= dq( 7 downto 0)'delayed(1 ps); + end if; + end process; + + + ---------------------------------------------------------------------------- + -- Check for erroneous usage + + -- TODO: check T_AVH, T_AVS, T_CVS, T_VP, T_AS + assert adv_n = '0' + report "ADV# not yet supported" + severity error; + + assert clk = '0' + report "Synchronous operation not yet supported" + severity error; + + + -- Should (probably) not assert oe_n and we_n at the same time + process (oe_n, we_n) + begin + assert not (oe_n = '0' and we_n = '0') + report "OE# and WE# are active simultaneously (write takes precedence)" + severity warning; + end process; + + -- Maximum CE# pulse width (to allow internal refresh) + process (ce_n) + begin + if rising_edge(ce_n) then + assert ce_n'last_event < T_CEM + report "T_CEM not met (Maximum CE# pulse width)" + severity error; + end if; + end process; + + + -- Write recovery time (address hold time) + process (a, internal_we_ub_n, internal_we_lb_n) + begin + if a'event then + if internal_we_ub_n = '1' then + assert internal_we_ub_n'last_event >= T_WR + report "T_WR not met (Write recovery time)" + severity error; + end if; + if internal_we_lb_n = '1' then + assert internal_we_lb_n'last_event >= T_WR + report "T_WR not met (Write recovery time)" + severity error; + end if; + end if; + end process; + + -- Address valid to end of WRITE + process (a, internal_we_ub_n, internal_we_lb_n) + begin + if rising_edge(internal_we_ub_n) then + assert a'last_event >= T_AW + report "T_AW not met (Address valid to end of WRITE)" + severity error; + end if; + if rising_edge(internal_we_lb_n) then + assert a'last_event >= T_AW + report "T_AW not met (Address valid to end of WRITE)" + severity error; + end if; + end process; + + -- Chip enable to end of WRITE + process (ce_n, internal_we_ub_n, internal_we_lb_n) + begin + if rising_edge(internal_we_ub_n) then + assert ce_n'delayed'delayed'last_event >= T_CW + report "T_CW not met (Chip enable to end of WRITE)" + severity error; + end if; + if rising_edge(internal_we_lb_n) then + assert ce_n'delayed'delayed'last_event >= T_CW + report "T_CW not met (Chip enable to end of WRITE)" + severity error; + end if; + end process; + + -- CE# HIGH between subsequent async operations + process (ce_n) + begin + if falling_edge(ce_n) then + assert ce_n'delayed'last_event >= T_CPH + report "T_CPH not met (CE# HIGH between subsequent async operations)" + severity error; + end if; + end process; + + -- LB#/UB# select to end of WRITE + process (ub_n, lb_n, internal_we_ub_n, internal_we_lb_n) + begin + if rising_edge(internal_we_ub_n) then + assert ub_n'last_event >= T_BW + report "T_BW not met (UB# select to end of WRITE)" + severity error; + end if; + if rising_edge(internal_we_lb_n) then + assert lb_n'last_event >= T_BW + report "T_BW not met (LB# select to end of WRITE)" + severity error; + end if; + end process; + + -- WRITE pulse width + process (we_n, internal_we_ub_n, internal_we_lb_n) + variable t: time; + begin + -- Datasheet specifies WE# HIGH pulse width strictly on WE# signal + -- FIXME: should this be strictly on internal we signals? + if falling_edge(we_n) then + assert we_n'delayed'last_event >= T_WPH + report "T_WPH not met (WRITE pulse width HIGH)" + severity error; + end if; + -- Datasheet specifies WE# LOW pulse width against whatever signal triggers write + -- FIXME: should this be strictly on internal we signals? + if rising_edge(internal_we_ub_n) then + assert we_n'delayed'delayed'last_event >= T_WP + report "T_WP not met (WRITE pulse width)" + severity error; + end if; + if rising_edge(internal_we_lb_n) then + t := we_n'delayed'delayed'last_event; + assert we_n'delayed'delayed'last_event >= T_WP + report "T_WP not met (WRITE pulse width)" & time'image(t) + severity error; + end if; + end process; + + -- Data setup and hold times + process (dq, internal_we_ub_n, internal_we_lb_n) + begin + if rising_edge(internal_we_ub_n) then + assert dq(15 downto 8)'delayed'delayed'last_event >= T_DW + report "T_DW not met (Data upper byte WRITE setup time)" + severity error; + end if; + if rising_edge(internal_we_lb_n) then + assert dq(7 downto 0)'delayed'delayed'last_event >= T_DW + report "T_DW not met (Data lower byte WRITE setup time)" + severity error; + end if; + if dq(15 downto 8)'event then + assert internal_we_ub_n'last_event >= T_DH + report "T_DH not met (Data upper byte HOLD from WRITE time)" + severity error; + end if; + if dq(7 downto 0)'event then + assert internal_we_lb_n'last_event >= T_DH + report "T_DH not met (Data lower byte HOLD from WRITE time)" + severity error; + end if; + end process; + +end behavioral; diff --git a/libraries/simulated/ps2_device.vhd b/libraries/simulated/ps2_device.vhd new file mode 100644 index 0000000..fe79f68 --- /dev/null +++ b/libraries/simulated/ps2_device.vhd @@ -0,0 +1,209 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.std_logic_misc.all; + + +entity ps2_device is + port ( + tx_byte: in std_logic_vector(7 downto 0); + + ps2_clk: inout std_logic; + ps2_data: inout std_logic + ); +end ps2_device; + + +architecture behavioral of ps2_device is + + type state_t is ( + S_IDLE, S_INHIBIT, + S_TX_BIT_L, S_TX_BIT_H, S_TX_STOP_L, S_TX_STOP_H, + S_RX_BIT_L, S_RX_BIT_H, S_RX_STOP_L + ); + + -- Timings from http://www.mcamafia.de/pdf/ibm_hitrc07.pdf + -- Slowest timing: + --constant T1: time := 25 us; -- Time from DATA transition to falling edge of CLK + --constant T2: time := 45 us; -- Time from rising edge of CLK to DATA transition + --constant T3: time := 50 us; -- Duration of CLK inactive (Tx) + --constant T4: time := 50 us; -- Duration of CLK active (Tx) + --constant T5: time := 50 us; + --constant T7: time := 50 us; -- Duration of CLK inactive (Rx) + --constant T8: time := 50 us; -- Duration of CLK active (Rx) + --constant T9: time := 25 us; -- Time after CLK rise to sample + + -- Fastest timing: + constant T1: time := 5 us; -- Time from DATA transition to falling edge of CLK + constant T2: time := 5 us; -- Time from rising edge of CLK to DATA transition + constant T3: time := 30 us; -- Duration of CLK inactive (Tx) + constant T4: time := 30 us; -- Duration of CLK active (Tx) + --constant T5: time := 0 us; + constant T7: time := 30 us; -- Duration of CLK inactive (Rx) + constant T8: time := 30 us; -- Duration of CLK active (Rx) + constant T9: time := 5 us; -- Time after CLK rise to sample + + signal cur_state: state_t := S_IDLE; + + signal tx_req: boolean; + + -- CLK and DATA that the device is *trying* to drive, can be overridden by the host + signal dev_clk: std_logic := '1'; + signal dev_data: std_logic := '1'; + + -- For detecting when the host overrides device's CLK and DATA + signal inhibit: std_logic; + signal host_tx: std_logic; + + signal test_sample: std_logic; + + signal data: std_logic_vector(10 downto 0); + signal i: integer; + +begin + + test_sample <= ps2_data'delayed(T8 - T9); + + process (cur_state, inhibit, host_tx, tx_req, tx_byte) + --variable data: std_logic_vector(10 downto 0); + --variable i: integer; + variable temp: std_logic; + begin + if inhibit'event then + if inhibit = '1' then + -- Abort any transmissions when inhibited + if cur_state /= S_IDLE then + report "PS2 device inhibited (transmission aborted)"; + else + report "PS2 device inhibited"; + end if; + if cur_state = S_TX_STOP_L or cur_state = S_TX_STOP_H then + -- FIXME: prevent inhibition during the stop bit, reporting is fine for now + report "PS2 device inhibited during stop bit" severity error; + end if; + cur_state <= S_INHIBIT; + else + report "PS2 device uninhibited"; + if host_tx = '1' then + i <= 0; + data <= (others => '0'); + cur_state <= S_RX_BIT_L after T8; + else + cur_state <= S_IDLE; + end if; + end if; + elsif tx_req'event and tx_req then + report "PS2 device beginning transmission"; + data <= '1' & '1' & (not xor_reduce(tx_byte)) & tx_byte; + i <= 0; + dev_data <= '0'; + cur_state <= S_TX_BIT_L after T1; + elsif host_tx'event and host_tx = '1' then + if not (cur_state = S_IDLE or cur_state = S_INHIBIT or + cur_state = S_RX_BIT_L or cur_state = S_RX_BIT_H or cur_state = S_RX_STOP_L) then + report "PS2 device saw unexpected drop in DATA line" severity error; + elsif cur_state = S_IDLE then + -- http://www.mcamafia.de/pdf/ibm_hitrc07.pdf *implies* that the + -- device should still receive data without the host pulling the + -- clock low first, but this is unusual and could cause problems + -- if the device is about to begin a transmission. + report "PS2 device saw drop in DATA line while not inhibited" severity warning; + i <= 0; + data <= (others => '0'); + cur_state <= S_RX_BIT_L after T8; + end if; + elsif cur_state'event then + -- Self-driving state machine, can be pushed into states by the rest of the process + case cur_state is + when S_IDLE => + dev_clk <= '1'; + dev_data <= '1'; + + when S_TX_BIT_L => + dev_clk <= '0'; + if i < 9 then + cur_state <= S_TX_BIT_H after T3; + else + cur_state <= S_TX_STOP_H after T3; + end if; + + when S_TX_BIT_H => + dev_clk <= '1'; + dev_data <= data(i) after T2; + i <= i + 1; + cur_state <= S_TX_BIT_L after T4; + + when S_TX_STOP_H => + dev_clk <= '1'; + dev_data <= '1' after T2; + cur_state <= S_TX_STOP_L after T4; + + when S_TX_STOP_L => + dev_clk <= '0'; + cur_state <= S_IDLE after T3; + + when S_INHIBIT => + dev_clk <= '1'; + dev_data <= '1'; + + when S_RX_BIT_L => + dev_clk <= '0'; + data(i) <= ps2_data'delayed(T8 - T9); -- Reach back into the previous clock high period + i <= i + 1; + cur_state <= S_RX_BIT_H after T7; + + when S_RX_BIT_H => + dev_clk <= '1'; + if i <= 9 then + cur_state <= S_RX_BIT_L after T8; + else + cur_state <= S_RX_STOP_L after T8; + end if; + + when S_RX_STOP_L => + dev_clk <= '0'; + dev_data <= '0'; -- Pull DATA low to acknowledge + data(i) <= ps2_data'delayed(T8 - T9); -- Reach back into the previous clock high period + temp := ps2_data'delayed(T8 - T9); + report "PS2 device recieved " & integer'image(to_integer(unsigned(data(8 downto 1)))); + -- Spec says device should continue clocking and request a + -- resend if it gets a zero stop bit, but that's already an + -- error condition so reporting is sufficient. + assert temp /= '0' + report "PS2 device got a 0 stop bit" + severity error; + assert xor_reduce(data(9 downto 1)) = '1' + report "PS2 device received wrong parity" + severity error; + cur_state <= S_IDLE after T7; + + when others => + cur_state <= S_IDLE; + end case; + end if; + end process; + + -- Check for anything driving the CLK and DATA lines too hard + process (ps2_clk, ps2_data) + begin + if ps2_clk = '1' then + report "PS2 clock line driven harder than expected" severity error; + end if; + if ps2_data = '1' then + report "PS2 data line driven harder than expected" severity error; + end if; + end process; + + -- Weaken clock and data high signals + ps2_clk <= '0' when dev_clk = '0' else 'H'; + ps2_data <= '0' when dev_data = '0' else 'H'; + + -- Detect host signaling + -- Needs delayed dev_clk/data to account for "gate delay" from computing ps2_clk/data from dev_clk/data + inhibit <= '1' when ps2_clk = '0' and dev_clk'delayed = '1' else '0'; + host_tx <= '1' when ps2_data = '0' and dev_data'delayed = '1' else '0'; + + -- Detect test process transmit request + tx_req <= not tx_byte'quiet; + +end behavioral; diff --git a/libraries/simulated/tests/test_js28f128j3d75.vhd b/libraries/simulated/tests/test_js28f128j3d75.vhd new file mode 100644 index 0000000..b84c0d1 --- /dev/null +++ b/libraries/simulated/tests/test_js28f128j3d75.vhd @@ -0,0 +1,181 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library flash_js28f128; + + +entity testbench_j28f128j3d75 is +end testbench_j28f128j3d75; + + +architecture behavior of testbench_js28f128j3d75 is + + type op is (O_NONE, O_READ_ARRAY, O_READ_STATUS, O_PROG_A, O_PROG_B, O_MODE_ARRAY); + + -- Flash signals + signal a: std_logic_vector(23 downto 0); + signal d: std_logic_vector(15 downto 0); + signal ce: std_logic_vector(2 downto 0); + signal rp_n: std_logic; + signal oe_n: std_logic; + signal we_n: std_logic; + signal sts: std_logic; + signal byte_n: std_logic; + signal vpen: std_logic; + + -- Debug + signal cur_op: op; + +begin + + uut: entity flash_js28f128.js28f128j3d75 port map ( + a => a, + d => d, + ce => ce, + rp_n => rp_n, + oe_n => oe_n, + we_n => we_n, + sts => sts, + byte_n => byte_n, + vpen => vpen + ); + + + tb: process + begin + -- Initial values + cur_op <= O_NONE; + a <= (others => '0'); + d <= (others => 'Z'); + ce <= (others => '1'); + oe_n <= '1'; + we_n <= '1'; + byte_n <= '1'; + vpen <= '1'; + + -- Reset + rp_n <= '1'; + wait for 1 us; + rp_n <= '0'; + wait for 25 us; + rp_n <= '1'; + wait for 210 ns; + + -- Wait for long BYTE# signal propagation time + wait for 1 ms; + + -- Asynchronous array read + cur_op <= O_READ_ARRAY; + ce(0) <= '0'; + oe_n <= '0'; + a <= x"000000"; + wait for 100 ns; + a <= x"000002"; + wait for 100 ns; + a <= x"000004"; + wait for 100 ns; + a <= x"000006"; + wait for 100 ns; + a <= x"000008"; + wait for 100 ns; + a <= x"00000a"; + wait for 100 ns; + a <= x"00000c"; + wait for 100 ns; + a <= x"00000e"; + wait for 100 ns; + ce(0) <= '1'; + oe_n <= '1'; + cur_op <= O_NONE; + + wait for 25 ns; + + -- Program word + ce(0) <= '0'; + + cur_op <= O_PROG_A; + a <= x"000000"; + d <= x"0040"; + we_n <= '0'; + wait for 60 ns; + we_n <= '1'; + wait for 30 ns; + + cur_op <= O_PROG_B; + a <= x"000000"; + d <= x"abcd"; + we_n <= '0'; + wait for 60 ns; + we_n <= '1'; + wait for 30 ns; + + ce(0) <= '1'; + d <= (others => 'Z'); + cur_op <= O_NONE; + wait for 100 us; + + -- Read status during program + cur_op <= O_READ_STATUS; + ce(0) <= '0'; + oe_n <= '0'; + a <= x"000000"; + wait for 100 ns; + assert d = x"0000" + report "Status read got wrong value: " & integer'image(to_integer(unsigned(d))) + severity error; + oe_n <= '1'; + ce(0) <= '1'; + cur_op <= O_NONE; + wait for 25 ns; + + -- Wait for program to complete + wait until sts /= '0'; + wait for 25 ns; + + -- Read status after program + cur_op <= O_READ_STATUS; + ce(0) <= '0'; + oe_n <= '0'; + a <= x"000000"; + wait for 100 ns; + assert d = x"0080" + report "Status read got wrong value: " & integer'image(to_integer(unsigned(d))) + severity error; + oe_n <= '1'; + ce(0) <= '1'; + cur_op <= O_NONE; + wait for 25 ns; + + -- Enter read-array mode + cur_op <= O_MODE_ARRAY; + a <= x"000000"; + d <= x"00ff"; + ce(0) <= '0'; + we_n <= '0'; + wait for 60 ns; + we_n <= '1'; + wait for 30 ns; + ce(0) <= '1'; + d <= (others => 'Z'); + cur_op <= O_NONE; + wait for 25 ns; + + -- Read flash array value + cur_op <= O_READ_ARRAY; + ce(0) <= '0'; + oe_n <= '0'; + a <= x"000000"; + wait for 100 ns; + assert d = x"abcd" + report "Array read got wrong value" + severity error; + oe_n <= '1'; + ce(0) <= '1'; + cur_op <= O_NONE; + wait for 25 ns; + + wait; + end process; + +end architecture; diff --git a/libraries/simulated/tests/test_mt45w8mw16bgx.vhd b/libraries/simulated/tests/test_mt45w8mw16bgx.vhd new file mode 100644 index 0000000..d045fb1 --- /dev/null +++ b/libraries/simulated/tests/test_mt45w8mw16bgx.vhd @@ -0,0 +1,92 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library simulated; + + +entity testbench_ram is +end testbench_ram; + + +architecture behavior of testbench_ram is + + signal addr: std_logic_vector(23 downto 0); -- Extra bit to allow hex literals + signal data: std_logic_vector(15 downto 0); + signal ce_n: std_logic; + signal oe_n: std_logic; + signal we_n: std_logic; + +begin + + tb: process + begin + addr <= x"000000"; + data <= (others => 'Z'); + ce_n <= '1'; + oe_n <= '1'; + we_n <= '1'; + --wait for 150 us; -- T_PU, initialization period + wait for 20 ns; + + addr <= x"000000"; + data <= x"dead"; + ce_n <= '0'; + we_n <= '0'; + wait for 70 ns; + ce_n <= '1'; + we_n <= '1'; + data <= (others => 'Z'); + wait for 10 ns; + + addr <= x"000001"; + data <= x"beef"; + ce_n <= '0'; + we_n <= '0'; + wait for 70 ns; + ce_n <= '1'; + we_n <= '1'; + data <= (others => 'Z'); + wait for 10 ns; + + addr <= x"000000"; + ce_n <= '0'; + oe_n <= '0'; + wait for 70 ns; + wait for 5 ns; + assert data = x"dead"; + wait for 5 ns; + ce_n <= '1'; + oe_n <= '1'; + wait for 5 ns; + + addr <= x"000001"; + ce_n <= '0'; + oe_n <= '0'; + wait for 70 ns; + wait for 5 ns; + assert data = x"beef"; + wait for 5 ns; + ce_n <= '1'; + oe_n <= '1'; + wait for 5 ns; + + wait; + end process; + + uut: entity simulated.mt45w8mw16bgx + port map ( + a => addr(22 downto 0), + dq => data, + clk => '0', + adv_n => '0', + cre => '0', + ce_n => ce_n, + oe_n => oe_n, + we_n => we_n, + lb_n => '0', + ub_n => '0', + rwait => open + ); + +end architecture; diff --git a/libraries/simulated/tests/test_ps2_device.vhd b/libraries/simulated/tests/test_ps2_device.vhd new file mode 100644 index 0000000..46418f7 --- /dev/null +++ b/libraries/simulated/tests/test_ps2_device.vhd @@ -0,0 +1,93 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity test_ps2_device is +end test_ps2_device; + + +architecture behavior of test_ps2_device is + + signal tx_byte: std_logic_vector(7 downto 0) := x"00"; + signal ps2_clk: std_logic; + signal ps2_data: std_logic; + +begin + + ps2_clk <= 'H'; + ps2_data <= 'H'; + + p_test: process + begin + ps2_clk <= 'Z'; + ps2_data <= 'Z'; + + -- Receive bytes form device + wait for 100 ns; + tx_byte <= x"a5"; + wait for 1 ms; + tx_byte <= x"a5"; + wait for 400 us; + ps2_clk <= '0'; -- Cancel transmission from host + wait for 400 us; + ps2_clk <= 'Z'; + wait for 200 us; + tx_byte <= x"c3"; + wait for 1 ms; + + -- Transmit byte to device + ps2_clk <= '0'; + wait for 30 us; + ps2_data <= '0'; -- start + wait for 30 us; + ps2_clk <= 'Z'; + wait until ps2_clk = '0'; + ps2_data <= '0'; -- 0 + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= 'Z'; -- 1 + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= '0'; -- 2 + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= 'Z'; -- 3 + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= '0'; -- 4 + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= '0'; -- 5 + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= 'Z'; -- 6 + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= 'Z'; -- 7 + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= 'Z'; -- parity + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= '0'; -- stop + wait until ps2_clk /= '0'; + wait until ps2_clk = '0'; + ps2_data <= 'Z'; -- ack + assert ps2_data = '0' + report "NACK from device" + severity error; + wait until ps2_clk /= '0'; + wait; + end process; + + e_ps2_device: entity work.ps2_device + port map ( + fast => true, + + tx_byte => tx_byte, + + ps2_clk => ps2_clk, + ps2_data => ps2_data + ); + +end; diff --git a/libraries/simulated/tests/test_simmem.vhd b/libraries/simulated/tests/test_simmem.vhd new file mode 100644 index 0000000..e00ba09 --- /dev/null +++ b/libraries/simulated/tests/test_simmem.vhd @@ -0,0 +1,70 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + + +entity test_simmem is +end test_simmem; + + +architecture behavior of test_simmem is + + signal rst_i: std_logic; + signal clk_i: std_logic; + signal cyc_i: std_logic; + signal stb_i: std_logic; + signal we_i: std_logic; + signal ack_o: std_logic; + signal adr_i: std_logic_vector(4 downto 0); + signal dat_i: std_logic_vector(7 downto 0); + signal dat_o: std_logic_vector(7 downto 0); + +begin + + p_test: process + begin + -- Initial values + cyc_i <= '0'; + stb_i <= '0'; + we_i <= '0'; + adr_i <= (others => '0'); + dat_i <= (others => '0'); + + -- Reset + rst_i <= '1'; + wait for 40 ns; + rst_i <= '0'; + + -- Done + wait; + end process; + + + e_uut: entity work.wb_memory + generic map ( + INIT => x"00112233445566778899aabbccddeeff0123456789abcdeffedcba9876543210" + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + cyc_i => cyc_i, + stb_i => stb_i, + we_i => we_i, + ack_o => ack_o, + adr_i => adr_i, + dat_i => dat_i, + dat_o => dat_o + ); + + + p_clk: process + begin + clk_i <= '0'; + wait for 10 ns; + clk_i <= '1'; + wait for 10 ns; + end process; + +end; diff --git a/libraries/simulated/wb_memory.vhd b/libraries/simulated/wb_memory.vhd new file mode 100644 index 0000000..13a8dcc --- /dev/null +++ b/libraries/simulated/wb_memory.vhd @@ -0,0 +1,49 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity wb_memory is + generic ( + INIT: std_logic_vector(32*8-1 downto 0) := (others => '0') + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(4 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0) + ); +end wb_memory; + + +architecture behavioral of wb_memory is + + type mem_array_t is array(natural range <>) of std_logic_vector(7 downto 0); + + signal mem_array: mem_array_t(31 downto 0); + +begin + + process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + for i in 0 to 31 loop + mem_array(31-i) <= INIT((i+1)*8-1 downto i*8); + end loop; + elsif cyc_i = '1' and stb_i = '1' and we_i = '1' then + mem_array(to_integer(unsigned(adr_i))) <= dat_i; + end if; + end if; + end process; + + ack_o <= '1'; + dat_o <= mem_array(to_integer(unsigned(adr_i))); + +end behavioral; diff --git a/libraries/utility/debounce.vhd b/libraries/utility/debounce.vhd new file mode 100644 index 0000000..375abbd --- /dev/null +++ b/libraries/utility/debounce.vhd @@ -0,0 +1,39 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity debounce is + port ( + clk: in std_logic; -- System clock + clk_samp: in std_logic; -- Sample clock, running slower than (but in sync with) clk_i + + btn_deb: out std_logic; -- Debounced button signal + btn_rise: out std_logic; -- Pulse for one period of clk_i on rising-edge of btn_o + btn_fall: out std_logic; -- Pulse for one period of clk_i on falling-edge of btn_o + + btn: in std_logic -- Input from bouncy button or switch + ); +end debounce; + + +architecture behavioral of debounce is + + signal edge_reg: std_logic_vector(1 downto 0); + +begin + + process (clk, clk_samp, btn, edge_reg) + begin + if rising_edge(clk_samp) then + edge_reg(0) <= btn; + end if; + if rising_edge(clk) then + edge_reg(1) <= edge_reg(0); + end if; + end process; + + btn_rise <= '1' when edge_reg = "01" else '0'; + btn_fall <= '1' when edge_reg = "10" else '0'; + btn_deb <= edge_reg(0); + +end behavioral; diff --git a/libraries/utility/fifo.vhd b/libraries/utility/fifo.vhd new file mode 100644 index 0000000..ae2a954 --- /dev/null +++ b/libraries/utility/fifo.vhd @@ -0,0 +1,175 @@ +-- TODO: -- TODO: https://eecs.umich.edu/courses/doing_dsp/handout/SRL16E.pdf +-- Play with using shift-registers to make this more dense +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + + +entity fifo is + generic ( + WIDTH: integer := 8; + COUNT: integer := 4 + ); + port ( + -- Not a Wishbone bus, but can still use the global Wishbone SYSCON + rst_i: in std_logic; + clk_i: in std_logic; + + -- Data inserted at head + h_stb_i: in std_logic; + h_ack_o: out std_logic; + h_dat_i: in std_logic_vector(WIDTH-1 downto 0); + + -- Data removed from tail + t_stb_o: out std_logic; + t_ack_i: in std_logic; + t_dat_o: out std_logic_vector(WIDTH-1 downto 0); + + -- Informational signals + is_empty: out std_logic; + is_full: out std_logic + ); +end fifo; + + +architecture behavioral of fifo is + + type data_array_t is array(natural range <>) of std_logic_vector(WIDTH-1 downto 0); + + signal data_array_reg: data_array_t(COUNT-1 downto 0); + signal data_array_shift: std_logic; + + signal index_reg: unsigned(4 downto 0); + signal index_up: std_logic; + signal index_down: std_logic; + + signal full: std_logic; + signal empty: std_logic; + +begin + + -- Wishbone-like interface + process (h_stb_i, t_ack_i) + begin + index_down <= '0'; + index_up <= '0'; + + if h_stb_i = '1' and full = '0' then + index_up <= '1'; + end if; + if t_ack_i = '1' and empty = '0' then + index_down <= '1'; + end if; + end process; + + t_stb_o <= not empty; + h_ack_o <= not full; + + is_empty <= empty; + is_full <= full; + + + -- Shift register of values + -- Cannot have reset or parallel-load logic if it's to be inferred as a LUT-shift-register + process (clk_i, index_up, data_array_reg, h_dat_i) + begin + if rising_edge(clk_i) then + if index_up = '1' then + for i in COUNT-1 downto 1 loop + data_array_reg(i) <= data_array_reg(i-1); + end loop; + data_array_reg(0) <= h_dat_i; + end if; + end if; + end process; + + t_dat_o <= data_array_reg(to_integer(index_reg(3 downto 0))); + + + -- Index counter + process (rst_i, clk_i, index_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + index_reg <= "01111"; + else + if index_up = '1' and index_down = '0' then + index_reg <= index_reg + 1; + elsif index_up = '0' and index_down = '1' then + index_reg <= index_reg - 1; + end if; + end if; + end if; + end process; + + + -- Emptiness/fullness detector + empty <= not index_reg(4); + full <= and_reduce(std_logic_vector(index_reg)); + +end behavioral; + + +architecture behavioral_old of fifo is + + type data_array is array(natural range <>) of std_logic_vector(WIDTH-1 downto 0); + + signal stall: std_logic_vector(COUNT-1 downto 0); + signal next_stall: std_logic_vector(COUNT-1 downto 0); + signal valid_reg: std_logic_vector(COUNT-1 downto 0); + signal prev_full: std_logic_vector(COUNT-1 downto 0); + signal data_array_reg: data_array(COUNT-1 downto 0); + signal prev_data: data_array(COUNT-1 downto 0); + signal h_ack: std_logic; + +begin + + -- A simple pipeline is easier to implement and smaller than a circular + -- buffer. This FIFO is also intended for either storing data coming in + -- much slower than the clock rate (RS232, PS2, SPI, I2C) before a + -- processor gets around to grabbing it, or queueing up bytes from a + -- processor before the interface gets around to sending it - so the latency + -- imposed by a short (4-8 element) pipeline is insignificant. If lower- + -- latency or more elements are needed the same entity can be used with a + -- different architecture. + + -- Stall logic is a combinational chain, so depth should be kept small + stall <= next_stall and valid_reg; + next_stall <= stall(COUNT-2 downto 0) & not t_ack_i; + + -- Convenience signals + -- Need to check h_ack in prev_full to prevent from "accepting" new valid + -- bit while also refusing to acknowledge because the FIFO is full + prev_full <= (h_stb_i and h_ack) & valid_reg(COUNT-1 downto 1); + prev_data <= h_dat_i & data_array_reg(COUNT-1 downto 1); + + -- Using !is_full for h_ack_o instead of the stall signal prevents the + -- combinational chain of stall signals from reaching all the way through. + -- This also means that a new entry cannot be added when the FIFO is full, + -- even if a value is being removed during the same cycle. Probably fine. + h_ack <= not and_reduce(valid_reg); + h_ack_o <= h_ack; + t_stb_o <= valid_reg(0); + t_dat_o <= data_array_reg(0); + is_empty <= not or_reduce(valid_reg); + is_full <= and_reduce(valid_reg); + + process (rst_i, clk_i, next_stall, prev_full, prev_data) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + valid_reg <= (others => '0'); + data_array_reg <= (others => (others => '0')); + else + for i in 0 to COUNT-1 loop + if stall(i) = '0' then + valid_reg(i) <= prev_full(i); + data_array_reg(i) <= prev_data(i); + end if; + end loop; + end if; + end if; + end process; + +end behavioral_old; diff --git a/libraries/utility/fifo16.vhd b/libraries/utility/fifo16.vhd new file mode 100644 index 0000000..8eea1bb --- /dev/null +++ b/libraries/utility/fifo16.vhd @@ -0,0 +1,108 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + + +entity fifo_16 is + generic ( + WIDTH: integer := 8 + ); + port ( + -- Not a Wishbone bus, but can still use the global Wishbone SYSCON + rst_i: in std_logic; + clk_i: in std_logic; + + -- Data inserted at head + h_stb_i: in std_logic; + h_ack_o: out std_logic; + h_dat_i: in std_logic_vector(WIDTH-1 downto 0); + + -- Data removed from tail + t_stb_o: out std_logic; + t_ack_i: in std_logic; + t_dat_o: out std_logic_vector(WIDTH-1 downto 0); + + -- Informational signals + is_empty: out std_logic; + is_full: out std_logic + ); +end fifo_16; + + +architecture behavioral of fifo_16 is + + type data_array_t is array(natural range <>) of std_logic_vector(WIDTH-1 downto 0); + + signal data_array_reg: data_array_t(15 downto 0); + signal data_array_shift: std_logic; + + signal index_reg: unsigned(4 downto 0); + signal index_up: std_logic; + signal index_down: std_logic; + + signal full: std_logic; + signal empty: std_logic; + +begin + + -- Wishbone-like interface + process (h_stb_i, t_ack_i, full, empty) + begin + index_down <= '0'; + index_up <= '0'; + + if h_stb_i = '1' and full = '0' then + index_up <= '1'; + end if; + if t_ack_i = '1' and empty = '0' then + index_down <= '1'; + end if; + end process; + + t_stb_o <= not empty; + h_ack_o <= not full; + + is_empty <= empty; + is_full <= full; + + + -- Shift register of values + -- Cannot have reset or parallel-access logic if it's to be inferred as a LUT-shift-register + process (clk_i, index_up, data_array_reg, h_dat_i) + begin + if rising_edge(clk_i) then + if index_up = '1' then + for i in 15 downto 1 loop + data_array_reg(i) <= data_array_reg(i-1); + end loop; + data_array_reg(0) <= h_dat_i; + end if; + end if; + end process; + + t_dat_o <= data_array_reg(to_integer(index_reg(3 downto 0))); + + + -- Index counter + process (rst_i, clk_i, index_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + index_reg <= "01111"; + else + if index_up = '1' and index_down = '0' then + index_reg <= index_reg + 1; + elsif index_up = '0' and index_down = '1' then + index_reg <= index_reg - 1; + end if; + end if; + end if; + end process; + + + -- Emptiness/fullness detector + empty <= not index_reg(4); + full <= and_reduce(std_logic_vector(index_reg)); + +end behavioral; diff --git a/libraries/utility/perf_counter.vhd b/libraries/utility/perf_counter.vhd new file mode 100644 index 0000000..d8b7f68 --- /dev/null +++ b/libraries/utility/perf_counter.vhd @@ -0,0 +1,199 @@ +-------------------------------------------------------------------------------- +-- Set of four 56-bit performance counters +-- +-- Each counter can be independantly configured to count: +-- - Rising edges +-- - Falling edges +-- - Clock cycles during which a signal is high +-- - Clock cycles during which a signal is low +-- +-- +---+---+---+---+---+---+---+---+ +-- | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +-- +---+---+---+---+---+---+---+---+ +-- 0x00 | |C3 |C2 |C1 |C0 | HALT +-- +---------------+---+---+---+---+ +-- 0x01 | |C3 |C2 |C1 |C0 | CLEAR +-- +---------------+---+---+---+---+ +-- 0x02 | |C3 |C2 |C1 |C0 | EDGE +-- +---------------+---+---+---+---+ +-- 0x03 | |C3 |C2 |C1 |C0 | PLRTY +-- +---------------+---+---+---+---+ +-- 0x04-0x0A | CTR0 (L.E.) | +-- +-------------------------------+ +-- 0x0B-0x11 | CTR1 (L.E.) | +-- +-------------------------------+ +-- 0x12-0x18 | CTR2 (L.E.) | +-- +-------------------------------+ +-- 0x19-0x1f | CTR3 (L.E.) | +-- +-------------------------------+ +-- +-- HALT - Set bits inhibit the corresponding counter from counting +-- CLEAR - Writing one clears the corresponding counter +-- EDGE - Set bits configure edge counting rather than cycles-at-level counting +-- PLRTY - Set bits configure counters to count high-level or rising-edges +-------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity perf_counter is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(4 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + trig: in std_logic_vector(3 downto 0) + ); +end perf_counter; + + +architecture behavioral of perf_counter is + + type count_array_t is array(natural range <>) of std_logic_vector(55 downto 0); + + -- Edge detection + signal prev_reg: std_logic_vector(3 downto 0); + signal rising: std_logic_vector(3 downto 0); + signal falling: std_logic_vector(3 downto 0); + + -- Wishbone-visible registers + signal halt: std_logic_vector(3 downto 0); + signal edge: std_logic_vector(3 downto 0); + signal plrty: std_logic_vector(3 downto 0); + signal counters: count_array_t(3 downto 0); + + -- Control signals + signal e_trig: std_logic_vector(3 downto 0); + signal l_trig: std_logic_vector(3 downto 0); + signal trigger: std_logic_vector(3 downto 0); + signal load_en: std_logic_vector(3 downto 0); + signal count_en: std_logic_vector(3 downto 0); + signal clear_en: std_logic_vector(3 downto 0); + +begin + + -- Wishbone bus + ack_o <= '1'; + + with adr_i select dat_o <= + "0000" & halt when "00000", + (others => '0') when "00001", + "0000" & edge when "00010", + "0000" & plrty when "00011", + counters(0)( 7 downto 0) when "00100", + counters(0)(15 downto 8) when "00101", + counters(0)(23 downto 16) when "00110", + counters(0)(31 downto 24) when "00111", + counters(0)(39 downto 32) when "01000", + counters(0)(47 downto 40) when "01001", + counters(0)(55 downto 48) when "01010", + counters(1)( 7 downto 0) when "01011", + counters(1)(15 downto 8) when "01100", + counters(1)(23 downto 16) when "01101", + counters(1)(31 downto 24) when "01110", + counters(1)(39 downto 32) when "01111", + counters(1)(47 downto 40) when "10000", + counters(1)(55 downto 48) when "10001", + counters(2)( 7 downto 0) when "10010", + counters(2)(15 downto 8) when "10011", + counters(2)(23 downto 16) when "10100", + counters(2)(31 downto 24) when "10101", + counters(2)(39 downto 32) when "10110", + counters(2)(47 downto 40) when "10111", + counters(2)(55 downto 48) when "11000", + counters(3)( 7 downto 0) when "11001", + counters(3)(15 downto 8) when "11010", + counters(3)(23 downto 16) when "11011", + counters(3)(31 downto 24) when "11100", + counters(3)(39 downto 32) when "11101", + counters(3)(47 downto 40) when "11110", + counters(3)(55 downto 48) when "11111", + (others => '1') when others; + + -- Edge detection + process (clk_i) + begin + if rising_edge(clk_i) then + prev_reg <= trig; + end if; + end process; + rising <= (not prev_reg) and trig; + falling <= prev_reg and (not trig); + + -- Count enable signals + e_trig <= (rising and plrty) or (falling and (not plrty)); -- Select rising or falling edge based on polarity reg + l_trig <= trig xor (not plrty); -- Invert level if low polarity is selected + trigger <= (e_trig and edge) or (l_trig and (not edge)); -- Select edge or level triggers based on edge reg + count_en <= trigger and (not halt); -- Mask trigger signal when halted + + -- Counter logic + process (rst_i, clk_i, + clear_en, count_en, counters, + cyc_i, stb_i, we_i, adr_i, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + for i in 0 to 3 loop + counters(i) <= (others => '0'); + end loop; + else + -- Wishbone writes need to be in here to have counter registers written by one process + if cyc_i = '1' and stb_i = '1' and we_i = '1' then + case adr_i is + when "00000" => halt <= dat_i(3 downto 0); + when "00001" => clear_en <= dat_i(3 downto 0); + when "00010" => edge <= dat_i(3 downto 0); + when "00011" => plrty <= dat_i(3 downto 0); + when "00100" => counters(0)( 7 downto 0) <= dat_i; + when "00101" => counters(0)(15 downto 8) <= dat_i; + when "00110" => counters(0)(23 downto 16) <= dat_i; + when "00111" => counters(0)(31 downto 24) <= dat_i; + when "01000" => counters(0)(39 downto 32) <= dat_i; + when "01001" => counters(0)(47 downto 40) <= dat_i; + when "01010" => counters(0)(55 downto 48) <= dat_i; + when "01011" => counters(1)( 7 downto 0) <= dat_i; + when "01100" => counters(1)(15 downto 8) <= dat_i; + when "01101" => counters(1)(23 downto 16) <= dat_i; + when "01110" => counters(1)(31 downto 24) <= dat_i; + when "01111" => counters(1)(39 downto 32) <= dat_i; + when "10000" => counters(1)(47 downto 40) <= dat_i; + when "10001" => counters(1)(55 downto 48) <= dat_i; + when "10010" => counters(2)( 7 downto 0) <= dat_i; + when "10011" => counters(2)(15 downto 8) <= dat_i; + when "10100" => counters(2)(23 downto 16) <= dat_i; + when "10101" => counters(2)(31 downto 24) <= dat_i; + when "10110" => counters(2)(39 downto 32) <= dat_i; + when "10111" => counters(2)(47 downto 40) <= dat_i; + when "11000" => counters(2)(55 downto 48) <= dat_i; + when "11001" => counters(3)( 7 downto 0) <= dat_i; + when "11010" => counters(3)(15 downto 8) <= dat_i; + when "11011" => counters(3)(23 downto 16) <= dat_i; + when "11100" => counters(3)(31 downto 24) <= dat_i; + when "11101" => counters(3)(39 downto 32) <= dat_i; + when "11110" => counters(3)(47 downto 40) <= dat_i; + when "11111" => counters(3)(55 downto 48) <= dat_i; + when others => null; + end case; + end if; + + -- Count and clear logic + for i in 0 to 3 loop + if clear_en(i) = '1' then + counters(i) <= (others => '0'); + elsif count_en(i) = '1' then + counters(i) <= std_logic_vector(unsigned(counters(i)) + 1); + end if; + end loop; + end if; + end if; + end process; + +end behavioral; diff --git a/libraries/utility/power_on_reset.vhd b/libraries/utility/power_on_reset.vhd new file mode 100644 index 0000000..f533533 --- /dev/null +++ b/libraries/utility/power_on_reset.vhd @@ -0,0 +1,31 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity power_on_reset is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + rst_o: out std_logic + ); +end power_on_reset; + + +architecture behavioral of power_on_reset is + + signal shift_reg: std_logic_vector(7 downto 0) := (others => '1'); + +begin + + process (rst_i, clk_i, shift_reg) + begin + if rst_i = '1' then + shift_reg <= (others => '1'); + elsif rising_edge(clk_i) then + shift_reg <= '0' & shift_reg(shift_reg'high downto 1); + end if; + end process; + + rst_o <= shift_reg(0); + +end behavioral; diff --git a/libraries/utility/tests/test_fifo.vhd b/libraries/utility/tests/test_fifo.vhd new file mode 100644 index 0000000..54bfa9e --- /dev/null +++ b/libraries/utility/tests/test_fifo.vhd @@ -0,0 +1,169 @@ +library ieee; +use ieee.std_logic_1164.all; + +library utility; +library work; + + +entity test_fifo is +end test_fifo; + + +architecture behavior of test_fifo is + + constant clk_i_period : time := 20 ns; + + signal rst: std_logic := '0'; + signal clk: std_logic := '0'; + + signal h_ack: std_logic; + signal h_stb: std_logic := '0'; + signal h_dat: std_logic_vector(7 downto 0) := (others => '0'); + + signal t_stb: std_logic; + signal t_ack: std_logic := '0'; + signal t_dat: std_logic_vector(7 downto 0); + + signal is_empty: std_logic; + signal is_full: std_logic; + +begin + + p_test: process + begin + h_stb <= '0'; + t_ack <= '0'; + h_dat <= x"00"; + + -- Reset + rst <= '1'; + wait for clk_i_period * 2; + rst <= '0'; + + -- Initial state + assert h_ack = '1' report "h_ack"; + assert t_stb = '0' report "t_stb"; + assert is_empty = '1' report "is_empty"; + assert is_full = '0' report "is_full"; + + -- Insert a value + h_dat <= x"a5"; + h_stb <= '1'; + wait for clk_i_period; + h_stb <= '0'; + + -- t_stb will still be '0' for the pipeline implementation, but don't + -- test that here so the test will still work for circular-buffer + -- implementations later, where tail_ready may be '1' immediately. + assert h_ack = '1' report "h_ack"; + assert is_empty = '0' report "is_empty"; + assert is_full = '0' report "is_full"; + + if t_stb = '0' then + wait until t_stb = '1'; + end if; + wait for clk_i_period * 3; + assert t_dat = x"a5" report "t_dat"; + + -- Remove the value + t_ack <= '1'; + wait for clk_i_period; + t_ack <= '0'; + + wait for clk_i_period * 2; + + -- Insert two values + h_dat <= x"5a"; + h_stb <= '1'; + wait for clk_i_period; + h_dat <= x"c3"; + wait for clk_i_period; + h_stb <= '0'; + + -- Remove one value + if t_stb = '0' then + wait until t_stb = '1'; + end if; + t_ack <= '1'; + wait for clk_i_period; + t_ack <= '0'; + wait for clk_i_period; + + -- Fill the queue + h_dat <= x"11"; + h_stb <= '1'; + wait for clk_i_period; + h_dat <= x"22"; + wait for clk_i_period; + h_dat <= x"33"; + wait for clk_i_period; + h_dat <= x"44"; + wait for clk_i_period; + h_dat <= x"55"; + wait for clk_i_period; + h_dat <= x"55"; + wait for clk_i_period; + h_dat <= x"66"; + wait for clk_i_period; + h_dat <= x"77"; + wait for clk_i_period; + h_dat <= x"88"; + wait for clk_i_period; + h_dat <= x"99"; + wait for clk_i_period; + h_dat <= x"aa"; + wait for clk_i_period; + h_dat <= x"bb"; + wait for clk_i_period; + h_dat <= x"cc"; + wait for clk_i_period; + h_dat <= x"dd"; + wait for clk_i_period; + h_dat <= x"ee"; + wait for clk_i_period; + --h_stb <= '0'; + --wait for 200 ns; + --h_stb <= '1'; + h_dat <= x"ff"; + wait for clk_i_period; + h_stb <= '0'; + + wait for clk_i_period; + + -- Remove one value + t_ack <= '1'; + wait for clk_i_period; + t_ack <= '0'; + + wait; + end process; + + e_uut: entity utility.fifo_16 + generic map ( + WIDTH => 8 + ) + port map ( + rst_i => rst, + clk_i => clk, + + h_stb_i => h_stb, + h_ack_o => h_ack, + h_dat_i => h_dat, + + t_stb_o => t_stb, + t_ack_i => t_ack, + t_dat_o => t_dat, + + is_empty => is_empty, + is_full => is_full + ); + + p_clk: process + begin + clk <= '0'; + wait for clk_i_period/2; + clk <= '1'; + wait for clk_i_period/2; + end process; + +end architecture; diff --git a/libraries/utility/tests/test_mapper.vhd b/libraries/utility/tests/test_mapper.vhd new file mode 100644 index 0000000..5b033fc --- /dev/null +++ b/libraries/utility/tests/test_mapper.vhd @@ -0,0 +1,202 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_mapper is +end test_mapper; + + +architecture behavior of test_mapper is + + signal cyc: std_logic; + signal stb: std_logic; + signal we: std_logic; + signal ack: std_logic; + signal adr: std_logic_vector(31 downto 0); + signal dat_mosi: std_logic_vector(7 downto 0); + signal dat_miso: std_logic_vector(7 downto 0); + + signal mem_cyc: std_logic; + signal mem_stb: std_logic; + signal mem_we: std_logic; + signal mem_ack: std_logic; + signal mem_adr: std_logic_vector(31 downto 0); + signal mem_mosi: std_logic_vector(7 downto 0); + signal mem_miso: std_logic_vector(7 downto 0); + + signal tile_cyc: std_logic; + signal tile_stb: std_logic; + signal tile_we: std_logic; + signal tile_ack: std_logic; + signal tile_adr: std_logic_vector(31 downto 0); + signal tile_mosi: std_logic_vector(7 downto 0); + signal tile_miso: std_logic_vector(7 downto 0); + + signal host_cyc: std_logic; + signal host_stb: std_logic; + signal host_we: std_logic; + signal host_ack: std_logic; + signal host_adr: std_logic_vector(31 downto 0); + signal host_mosi: std_logic_vector(7 downto 0); + signal host_miso: std_logic_vector(7 downto 0); + + signal ps2_cyc: std_logic; + signal ps2_stb: std_logic; + signal ps2_we: std_logic; + signal ps2_ack: std_logic; + signal ps2_adr: std_logic_vector(31 downto 0); + signal ps2_mosi: std_logic_vector(7 downto 0); + signal ps2_miso: std_logic_vector(7 downto 0); + + signal uart_cyc: std_logic; + signal uart_stb: std_logic; + signal uart_we: std_logic; + signal uart_ack: std_logic; + signal uart_adr: std_logic_vector(31 downto 0); + signal uart_mosi: std_logic_vector(7 downto 0); + signal uart_miso: std_logic_vector(7 downto 0); + +begin + + p_test: process + begin + -- Initial values + cyc <= '0'; + stb <= '0'; + we <= '0'; + adr <= x"00000000"; + dat_mosi <= x"00"; + mem_miso <= x"11"; + tile_miso <= x"aa"; + host_miso <= x"bb"; + ps2_miso <= x"cc"; + uart_miso <= x"dd"; + mem_ack <= '0'; + tile_ack <= '0'; + host_ack <= '0'; + ps2_ack <= '0'; + uart_ack <= '0'; + + cyc <= '1'; + stb <= '1'; + we <= '0'; + adr <= x"00000000"; + dat_mosi <= x"00"; + + wait for 100 ns; + + cyc <= '1'; + stb <= '1'; + we <= '0'; + adr <= x"02000000"; + dat_mosi <= x"00"; + + -- Done + wait; + end process; + + e_uut: entity work.wb_mapper + generic map ( + A_WIDTH => 32, + D_WIDTH => 8 + ) + port map ( + cyc_i => cyc, + stb_i => stb, + we_i => we, + ack_o => ack, + adr_i => adr, + dat_i => dat_mosi, + dat_o => dat_miso, + + -- Flash: 0x00000000-0x00ffffff + -- Ram: 0x01000000-0x01ffffff + mask_0 => "00000010000000000000000000000000", + match_0 => "00000000000000000000000000000000", + cyc_o_0 => mem_cyc, + stb_o_0 => mem_stb, + we_o_0 => mem_we, + ack_i_0 => mem_ack, + adr_o_0 => mem_adr, + dat_o_0 => mem_mosi, + dat_i_0 => mem_miso, + + -- Vbuf: 0x02000000-0x02001fff + -- Tiles: 0x02002000-0x02003fff + mask_1 => "00000010000000000100000000000000", + match_1 => "00000010000000000000000000000000", + cyc_o_1 => tile_cyc, + stb_o_1 => tile_stb, + we_o_1 => tile_we, + ack_i_1 => tile_ack, + adr_o_1 => tile_adr, + dat_o_1 => tile_mosi, + dat_i_1 => tile_miso, + + -- Host: 0x02004000-0x02004007 + mask_2 => "00000010000000000100000000011000", + match_2 => "00000010000000000100000000000000", + cyc_o_2 => host_cyc, + stb_o_2 => host_stb, + we_o_2 => host_we, + ack_i_2 => host_ack, + adr_o_2 => host_adr, + dat_o_2 => host_mosi, + dat_i_2 => host_miso, + + -- PS2: 0x02004008-0x0200400f + mask_3 => "00000010000000000100000000011000", + match_3 => "00000010000000000100000000001000", + cyc_o_3 => ps2_cyc, + stb_o_3 => ps2_stb, + we_o_3 => ps2_we, + ack_i_3 => ps2_ack, + adr_o_3 => ps2_adr, + dat_o_3 => ps2_mosi, + dat_i_3 => ps2_miso, + + -- UART: 0x02004010-0x02004017 + mask_4 => "00000010000000000100000000011000", + match_4 => "00000010000000000100000000010000", + cyc_o_4 => uart_cyc, + stb_o_4 => uart_stb, + we_o_4 => uart_we, + ack_i_4 => uart_ack, + adr_o_4 => uart_adr, + dat_o_4 => uart_mosi, + dat_i_4 => uart_miso, + + mask_5 => "00000000000000000000000000000000", + match_5 => "00000000000000000000000000000001", + cyc_o_5 => open, + stb_o_5 => open, + we_o_5 => open, + ack_i_5 => '1', + adr_o_5 => open, + dat_o_5 => open, + dat_i_5 => "00000000", + + mask_6 => "00000000000000000000000000000000", + match_6 => "00000000000000000000000000000001", + cyc_o_6 => open, + stb_o_6 => open, + we_o_6 => open, + ack_i_6 => '1', + adr_o_6 => open, + dat_o_6 => open, + dat_i_6 => "00000000", + + mask_7 => "00000000000000000000000000000000", + match_7 => "00000000000000000000000000000001", + cyc_o_7 => open, + stb_o_7 => open, + we_o_7 => open, + ack_i_7 => '1', + adr_o_7 => open, + dat_o_7 => open, + dat_i_7 => "00000000" + ); + +end; diff --git a/libraries/utility/wb_debug.vhd b/libraries/utility/wb_debug.vhd new file mode 100644 index 0000000..32bedae --- /dev/null +++ b/libraries/utility/wb_debug.vhd @@ -0,0 +1,64 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity wb_debug is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(2 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + debug_i: in std_logic_vector(63 downto 0); + debug_o: out std_logic_vector(63 downto 0) + ); +end wb_debug; + + +architecture behavioral of wb_debug is + + signal debug_reg: std_logic_vector(63 downto 0); + +begin + + process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + debug_reg <= (others => '0'); + elsif cyc_i = '1' and stb_i = '1' and we_i = '1' then + case adr_i is + when "000" => debug_reg( 7 downto 0) <= dat_i; + when "001" => debug_reg(15 downto 8) <= dat_i; + when "010" => debug_reg(23 downto 16) <= dat_i; + when "011" => debug_reg(31 downto 24) <= dat_i; + when "100" => debug_reg(39 downto 32) <= dat_i; + when "101" => debug_reg(47 downto 40) <= dat_i; + when "110" => debug_reg(55 downto 48) <= dat_i; + when others => debug_reg(63 downto 56) <= dat_i; + end case; + end if; + end if; + end process; + + with adr_i select dat_o <= + debug_i( 7 downto 0) when "000", + debug_i(15 downto 8) when "001", + debug_i(23 downto 16) when "010", + debug_i(31 downto 24) when "011", + debug_i(39 downto 32) when "100", + debug_i(47 downto 40) when "101", + debug_i(55 downto 48) when "110", + debug_i(63 downto 56) when others; + + ack_o <= '1'; + + debug_o <= debug_reg; + +end behavioral; diff --git a/libraries/utility/wb_mapper.vhd b/libraries/utility/wb_mapper.vhd new file mode 100644 index 0000000..87f68ff --- /dev/null +++ b/libraries/utility/wb_mapper.vhd @@ -0,0 +1,212 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity wb_mapper is + generic ( + A_WIDTH: integer := 32; + D_WIDTH: integer := 8 + ); + port ( + -- Master Wishbone interface + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(31 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + -- Device wishbone interfaces + mask_0: in std_logic_vector(A_WIDTH-1 downto 0); + match_0: in std_logic_vector(A_WIDTH-1 downto 0); + cyc_o_0: out std_logic; + stb_o_0: out std_logic; + we_o_0: out std_logic; + ack_i_0: in std_logic; + adr_o_0: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_0: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_0: in std_logic_vector(D_WIDTH-1 downto 0); + + mask_1: in std_logic_vector(A_WIDTH-1 downto 0); + match_1: in std_logic_vector(A_WIDTH-1 downto 0); + cyc_o_1: out std_logic; + stb_o_1: out std_logic; + we_o_1: out std_logic; + ack_i_1: in std_logic; + adr_o_1: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_1: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_1: in std_logic_vector(D_WIDTH-1 downto 0); + + mask_2: in std_logic_vector(A_WIDTH-1 downto 0); + match_2: in std_logic_vector(A_WIDTH-1 downto 0); + cyc_o_2: out std_logic; + stb_o_2: out std_logic; + we_o_2: out std_logic; + ack_i_2: in std_logic; + adr_o_2: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_2: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_2: in std_logic_vector(D_WIDTH-1 downto 0); + + mask_3: in std_logic_vector(A_WIDTH-1 downto 0); + match_3: in std_logic_vector(A_WIDTH-1 downto 0); + cyc_o_3: out std_logic; + stb_o_3: out std_logic; + we_o_3: out std_logic; + ack_i_3: in std_logic; + adr_o_3: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_3: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_3: in std_logic_vector(D_WIDTH-1 downto 0); + + mask_4: in std_logic_vector(A_WIDTH-1 downto 0); + match_4: in std_logic_vector(A_WIDTH-1 downto 0); + cyc_o_4: out std_logic; + stb_o_4: out std_logic; + we_o_4: out std_logic; + ack_i_4: in std_logic; + adr_o_4: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_4: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_4: in std_logic_vector(D_WIDTH-1 downto 0); + + mask_5: in std_logic_vector(A_WIDTH-1 downto 0); + match_5: in std_logic_vector(A_WIDTH-1 downto 0); + cyc_o_5: out std_logic; + stb_o_5: out std_logic; + we_o_5: out std_logic; + ack_i_5: in std_logic; + adr_o_5: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_5: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_5: in std_logic_vector(D_WIDTH-1 downto 0); + + mask_6: in std_logic_vector(A_WIDTH-1 downto 0); + match_6: in std_logic_vector(A_WIDTH-1 downto 0); + cyc_o_6: out std_logic; + stb_o_6: out std_logic; + we_o_6: out std_logic; + ack_i_6: in std_logic; + adr_o_6: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_6: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_6: in std_logic_vector(D_WIDTH-1 downto 0); + + mask_7: in std_logic_vector(A_WIDTH-1 downto 0); + match_7: in std_logic_vector(A_WIDTH-1 downto 0); + cyc_o_7: out std_logic; + stb_o_7: out std_logic; + we_o_7: out std_logic; + ack_i_7: in std_logic; + adr_o_7: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_7: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_7: in std_logic_vector(D_WIDTH-1 downto 0) + ); +end wb_mapper; + + +architecture behavioral of wb_mapper is + + signal sel_0: std_logic; + signal sel_1: std_logic; + signal sel_2: std_logic; + signal sel_3: std_logic; + signal sel_4: std_logic; + signal sel_5: std_logic; + signal sel_6: std_logic; + signal sel_7: std_logic; + signal dummy: std_logic; + + signal en_0: std_logic_vector(7 downto 0); + signal en_1: std_logic_vector(7 downto 0); + signal en_2: std_logic_vector(7 downto 0); + signal en_3: std_logic_vector(7 downto 0); + signal en_4: std_logic_vector(7 downto 0); + signal en_5: std_logic_vector(7 downto 0); + signal en_6: std_logic_vector(7 downto 0); + signal en_7: std_logic_vector(7 downto 0); + +begin + + sel_0 <= '1' when (adr_i and mask_0) = match_0 else '0'; + en_0 <= (others => sel_0); + cyc_o_0 <= cyc_i and sel_0; + stb_o_0 <= stb_i; + we_o_0 <= we_i; + adr_o_0 <= adr_i; + dat_o_0 <= dat_i; + + sel_1 <= '1' when (adr_i and mask_1) = match_1 else '0'; + en_1 <= (others => sel_1); + cyc_o_1 <= cyc_i and sel_1; + stb_o_1 <= stb_i; + we_o_1 <= we_i; + adr_o_1 <= adr_i; + dat_o_1 <= dat_i; + + sel_2 <= '1' when (adr_i and mask_2) = match_2 else '0'; + en_2 <= (others => sel_2); + cyc_o_2 <= cyc_i and sel_2; + stb_o_2 <= stb_i; + we_o_2 <= we_i; + adr_o_2 <= adr_i; + dat_o_2 <= dat_i; + + sel_3 <= '1' when (adr_i and mask_3) = match_3 else '0'; + en_3 <= (others => sel_3); + cyc_o_3 <= cyc_i and sel_3; + stb_o_3 <= stb_i; + we_o_3 <= we_i; + adr_o_3 <= adr_i; + dat_o_3 <= dat_i; + + sel_4 <= '1' when (adr_i and mask_4) = match_4 else '0'; + en_4 <= (others => sel_4); + cyc_o_4 <= cyc_i and sel_4; + stb_o_4 <= stb_i; + we_o_4 <= we_i; + adr_o_4 <= adr_i; + dat_o_4 <= dat_i; + + sel_5 <= '1' when (adr_i and mask_5) = match_5 else '0'; + en_5 <= (others => sel_5); + cyc_o_5 <= cyc_i and sel_5; + stb_o_5 <= stb_i; + we_o_5 <= we_i; + adr_o_5 <= adr_i; + dat_o_5 <= dat_i; + + sel_6 <= '1' when (adr_i and mask_6) = match_6 else '0'; + en_6 <= (others => sel_6); + cyc_o_6 <= cyc_i and sel_6; + stb_o_6 <= stb_i; + we_o_6 <= we_i; + adr_o_6 <= adr_i; + dat_o_6 <= dat_i; + + sel_7 <= '1' when (adr_i and mask_7) = match_7 else '0'; + en_7 <= (others => sel_7); + cyc_o_7 <= cyc_i and sel_7; + stb_o_7 <= stb_i; + we_o_7 <= we_i; + adr_o_7 <= adr_i; + dat_o_7 <= dat_i; + + dummy <= not (sel_0 or sel_1 or sel_2 or sel_3 or sel_4 or sel_5 or sel_6 or sel_7); + + ack_o <= (ack_i_0 and sel_0) or + (ack_i_1 and sel_1) or + (ack_i_2 and sel_2) or + (ack_i_3 and sel_3) or + (ack_i_4 and sel_4) or + (ack_i_5 and sel_5) or + (ack_i_6 and sel_6) or + (ack_i_7 and sel_7) or + dummy; -- If none are selected, acknowledge to eat the bus transaction + + dat_o <= (dat_i_0 and en_0) or + (dat_i_1 and en_1) or + (dat_i_2 and en_2) or + (dat_i_3 and en_3) or + (dat_i_4 and en_4) or + (dat_i_5 and en_5) or + (dat_i_6 and en_6) or + (dat_i_7 and en_7); + +end behavioral; diff --git a/libraries/utility/wb_mux2.vhd b/libraries/utility/wb_mux2.vhd new file mode 100644 index 0000000..e0eb31d --- /dev/null +++ b/libraries/utility/wb_mux2.vhd @@ -0,0 +1,43 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity wb_mux2 is + generic ( + WIDTH: integer := 8 + ); + port ( + sel: in std_logic; + + -- Single master + cyc_i: in std_logic; + ack_o: out std_logic; + dat_o: out std_logic_vector(WIDTH-1 downto 0); + + -- Multiple devices + cyc_o_0: out std_logic; + ack_i_0: in std_logic; + dat_i_0: in std_logic_vector(WIDTH-1 downto 0); + + cyc_o_1: out std_logic; + ack_i_1: in std_logic; + dat_i_1: in std_logic_vector(WIDTH-1 downto 0) + ); +end wb_mux2; + + +architecture behavioral of wb_mux2 is +begin + + cyc_o_0 <= cyc_i when sel = '0' else '0'; + cyc_o_1 <= cyc_i when sel /= '0' else '0'; + + with sel select dat_o <= + dat_i_0 when '0', + dat_i_1 when others; + + with sel select ack_o <= + ack_i_0 when '0', + ack_i_1 when others; + +end behavioral; diff --git a/libraries/utility/wb_mux4.vhd b/libraries/utility/wb_mux4.vhd new file mode 100644 index 0000000..53bf871 --- /dev/null +++ b/libraries/utility/wb_mux4.vhd @@ -0,0 +1,97 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity wb_mux4 is + generic ( + A_WIDTH: integer := 32; + D_WIDTH: integer := 8 + ); + port ( + sel: in std_logic_vector(1 downto 0); + + -- Wishbone interface to master + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(A_WIDTH-1 downto 0); + dat_i: in std_logic_vector(D_WIDTH-1 downto 0); + dat_o: out std_logic_vector(D_WIDTH-1 downto 0); + + -- Wishbone interfaces to devices + cyc_o_0: out std_logic; + stb_o_0: out std_logic; + we_o_0: in std_logic; + ack_i_0: out std_logic; + adr_o_0: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_0: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_0: in std_logic_vector(D_WIDTH-1 downto 0); + + cyc_o_1: out std_logic; + stb_o_1: out std_logic; + we_o_1: in std_logic; + ack_i_1: out std_logic; + adr_o_1: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_1: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_1: in std_logic_vector(D_WIDTH-1 downto 0); + + cyc_o_2: out std_logic; + stb_o_2: out std_logic; + we_o_2: in std_logic; + ack_i_2: out std_logic; + adr_o_2: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_2: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_2: in std_logic_vector(D_WIDTH-1 downto 0); + + cyc_o_3: out std_logic; + stb_o_3: out std_logic; + we_o_3: in std_logic; + ack_i_3: out std_logic; + adr_o_3: out std_logic_vector(A_WIDTH-1 downto 0); + dat_o_3: out std_logic_vector(D_WIDTH-1 downto 0); + dat_i_3: in std_logic_vector(D_WIDTH-1 downto 0) + ); +end wb_mux4; + + +architecture behavioral of wb_mux4 is +begin + + cyc_o_0 <= cyc_i when sel = "00" else '0'; + stb_o_0 <= stb_i; + we_o_0 <= we_i; + adr_o_0 <= adr_i; + dat_o_0 <= dat_i; + + cyc_o_1 <= cyc_i when sel = "01" else '0'; + stb_o_1 <= stb_i; + we_o_1 <= we_i; + adr_o_1 <= adr_i; + dat_o_1 <= dat_i; + + cyc_o_2 <= cyc_i when sel = "10" else '0'; + stb_o_2 <= stb_i; + we_o_2 <= we_i; + adr_o_2 <= adr_i; + dat_o_2 <= dat_i; + + cyc_o_3 <= cyc_i when sel = "11" else '0'; + stb_o_3 <= stb_i; + we_o_3 <= we_i; + adr_o_3 <= adr_i; + dat_o_3 <= dat_i; + + with sel select ack_o <= + ack_i_0 when "00", + ack_i_1 when "01", + ack_i_2 when "10", + ack_i_3 when others; + + with sel select dat_o <= + dat_i_0 when "00", + dat_i_1 when "01", + dat_i_2 when "10", + dat_i_3 when others; + +end behavioral; diff --git a/libraries/vga/tests/program.sh b/libraries/vga/tests/program.sh new file mode 100755 index 0000000..e2092f0 --- /dev/null +++ b/libraries/vga/tests/program.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +djtgcfg -d Nexys2 -i 0 -f test_nexys2.bit prog + diff --git a/libraries/vga/tests/test_nexys2.vhd b/libraries/vga/tests/test_nexys2.vhd new file mode 100644 index 0000000..d685c47 --- /dev/null +++ b/libraries/vga/tests/test_nexys2.vhd @@ -0,0 +1,86 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library nexys2_lib; + +library work; + + +entity test_nexys2 is + port ( + clk_50: in std_logic; + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + sw: in std_logic_vector(7 downto 0); + btn: in std_logic_vector(3 downto 0); + + vgaRed: out std_logic_vector(3 downto 1); + vgaGreen: out std_logic_vector(3 downto 1); + vgaBlue: out std_logic_vector(3 downto 2); + Hsync: out std_logic; + Vsync: out std_logic + ); +end test_nexys2; + + +architecture behavioral of test_nexys2 is + + signal clk: std_logic; + signal rst: std_logic; + signal cyc: std_logic; + signal stb: std_logic; + signal we: std_logic; + signal ack: std_logic; + signal adr_master: std_logic_vector(7 downto 0); + signal adr_slave: std_logic_vector(13 downto 0); + signal dat_mosi: std_logic_vector(7 downto 0); + signal dat_miso: std_logic_vector(7 downto 0); + +begin + + e_wb: entity nexys2_lib.wishbone_debugger + port map ( + clk_o => clk, + rst_o => rst, + + cyc_o => cyc, + stb_o => stb, + we_o => we, + ack_i => ack, + adr_o => adr_master, + dat_o => dat_mosi, + dat_i => dat_miso, + + clk_50 => clk_50, + seg => seg, + dp => dp, + an => an, + sw => sw, + btn => btn + ); + + adr_slave <= adr_master(7) & "000000" & adr_master(6 downto 0); + + e_dut: entity work.vga_tiler + port map ( + rst_i => rst, + clk_i => clk, + + cyc_i => cyc, + stb_i => stb, + we_i => we, + ack_o => ack, + adr_i => adr_slave, + dat_i => dat_mosi, + dat_o => dat_miso, + + vgaRed => vgaRed, + vgaGreen => vgaGreen, + vgaBlue => vgaBlue, + Hsync => Hsync, + Vsync => Vsync + ); + +end behavioral; diff --git a/libraries/vga/vga_counter.vhd b/libraries/vga/vga_counter.vhd new file mode 100644 index 0000000..907188e --- /dev/null +++ b/libraries/vga/vga_counter.vhd @@ -0,0 +1,184 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity vga_counter is + generic ( + -- Default settings for 640x480, 60Hz with 50MHz clock + + -- Horizontal timings in units of clock cycles + -- At 50MHz, 2 clocks per pixel (25MHz pixel clock) + H_DISPLAY: integer := 640*2; + H_FRONT_PORCH: integer := 16*2; + H_SYNC_WIDTH: integer := 96*2; + H_BACK_PORCH: integer := 48*2; + + -- Vertical timings in units of lines + V_DISPLAY: integer := 480; + V_FRONT_PORCH: integer := 10; + V_SYNC_WIDTH: integer := 2; + V_BACK_PORCH: integer := 29 + ); + port ( + clk_50: in std_logic; + + pix_x: out std_logic_vector(10 downto 0); + pix_y: out std_logic_vector(9 downto 0); + + h_blank: out std_logic; + h_sync: out std_logic; + + v_blank: out std_logic; + v_sync: out std_logic + ); +end vga_counter; + + +architecture behavioral of vga_counter is + + constant V_S_START: integer := V_DISPLAY + V_FRONT_PORCH; + constant V_S_END: integer := V_DISPLAY + V_FRONT_PORCH + V_SYNC_WIDTH; + constant V_TOTAL: integer := V_DISPLAY + V_FRONT_PORCH + V_SYNC_WIDTH + V_BACK_PORCH; + + constant H_S_START: integer := H_DISPLAY + H_FRONT_PORCH; + constant H_S_END: integer := H_DISPLAY + H_FRONT_PORCH + H_SYNC_WIDTH; + constant H_TOTAL: integer := H_DISPLAY + H_FRONT_PORCH + H_SYNC_WIDTH + H_BACK_PORCH; + + -- Using what are essentially JK flip flops for blank and sync signals + -- allows the signals to be valid immediately after a clock edge rather than + -- having to wait for propagation of the count value through comparators + -- with long carry chains. + -- The monitor probably wouldn't care - video signal timings can be loosey- + -- goosey sometimes, but for signalling the rest of the system internally, + -- like raster interrupts and such, it's best to keep combo-logic out of the + -- output signals when possible. + + -- The pixel clock is 25MHz, so the horizontal count reg has an extra bit + -- that should be stripped off when checking the current x position. + + -- There's no reset logic, but if it starts up with the counters out of + -- bounds, they'll overflow to zero on a short enough timescale (about one + -- frame) that it shouldn't be a problem. + + signal h_count_reg: unsigned(10 downto 0); + signal h_count_end: std_logic; + signal h_blank_reg: std_logic; + signal h_blank_start: std_logic; + signal h_blank_end: std_logic; + signal h_sync_reg: std_logic; + signal h_sync_start: std_logic; + signal h_sync_end: std_logic; + + signal v_count_reg: unsigned(9 downto 0); + signal v_count_en: std_logic; + signal v_count_end: std_logic; + signal v_blank_reg: std_logic; + signal v_blank_start: std_logic; + signal v_blank_end: std_logic; + signal v_sync_reg: std_logic; + signal v_sync_start: std_logic; + signal v_sync_end: std_logic; + +begin + + -- Horizontal timing + + process (clk_50, h_count_end, h_count_reg) + begin + if rising_edge(clk_50) then + if h_count_end = '1' then + h_count_reg <= (others => '0'); + else + h_count_reg <= h_count_reg + 1; + end if; + end if; + end process; + + process (clk_50, h_blank_start, h_blank_end) + begin + if rising_edge(clk_50) then + if h_blank_start = '1' then + h_blank_reg <= '1'; + elsif h_blank_end = '1' then + h_blank_reg <= '0'; + end if; + end if; + end process; + + process (clk_50, h_sync_start, h_sync_end) + begin + if rising_edge(clk_50) then + if h_sync_start = '1' then + h_sync_reg <= '1'; + elsif h_sync_end = '1' then + h_sync_reg <= '0'; + end if; + end if; + end process; + + h_blank_start <= '1' when h_count_reg = H_DISPLAY-1 else '0'; + h_sync_start <= '1' when h_count_reg = H_S_START-1 else '0'; + h_sync_end <= '1' when h_count_reg = H_S_END-1 else '0'; + h_blank_end <= '1' when h_count_reg = H_TOTAL-1 else '0'; + h_count_end <= '1' when h_count_reg = H_TOTAL-1 else '0'; + + + -- Advance the line count when the horrizontal sync pulse begins + v_count_en <= h_sync_start; + + + -- Vertical timing + + process (clk_50, v_count_en, v_count_end, v_count_reg) + begin + if rising_edge(clk_50) and v_count_en = '1' then + if v_count_end = '1' then + v_count_reg <= (others => '0'); + else + v_count_reg <= v_count_reg + 1; + end if; + end if; + end process; + + process (clk_50, v_blank_start, v_blank_end) + begin + if rising_edge(clk_50) then + if v_blank_start = '1' then + v_blank_reg <= '1'; + elsif v_blank_end = '1' then + v_blank_reg <= '0'; + end if; + end if; + end process; + + process (clk_50, v_sync_start, v_sync_end) + begin + if rising_edge(clk_50) then + if v_sync_start = '1' then + v_sync_reg <= '1'; + elsif v_sync_end = '1' then + v_sync_reg <= '0'; + end if; + end if; + end process; + + v_blank_start <= '1' when v_count_reg = V_DISPLAY-1 else '0'; + v_sync_start <= '1' when v_count_reg = V_S_START-1 else '0'; + v_sync_end <= '1' when v_count_reg = V_S_END-1 else '0'; + v_blank_end <= '1' when v_count_reg = V_TOTAL-1 else '0'; + v_count_end <= '1' when v_count_reg = V_TOTAL-1 else '0'; + + + -- Output signals + + pix_x <= std_logic_vector(h_count_reg); + pix_y <= std_logic_vector(v_count_reg); + + h_blank <= h_blank_reg; + h_sync <= h_sync_reg; + + v_blank <= v_blank_reg; + v_sync <= v_sync_reg; + +end behavioral; diff --git a/libraries/vga/vga_tiler.vhd b/libraries/vga/vga_tiler.vhd new file mode 100644 index 0000000..ae45160 --- /dev/null +++ b/libraries/vga/vga_tiler.vhd @@ -0,0 +1,530 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +library work; + + +entity vga_tiler is + port ( + -- Wishbone SYSCON + rst_i: in std_logic; + clk_i: in std_logic; -- 50MHz + + -- System access to video ram + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(13 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + -- VGA output + vgaRed: out std_logic_vector(3 downto 1); + vgaGreen: out std_logic_vector(3 downto 1); + vgaBlue: out std_logic_vector(3 downto 2); + Hsync: out std_logic; + Vsync: out std_logic + ); +end vga_tiler; + + +architecture behavioral of vga_tiler is + + -- System Wishbone interface + type wb_state_t is (S_IDLE, S_ACK); + signal wb_state_reg: wb_state_t; + signal wb_state_next: wb_state_t; + signal char0_stb_i: std_logic; + signal char1_stb_i: std_logic; + signal char2_stb_i: std_logic; + signal char3_stb_i: std_logic; + signal tile_stb_i: std_logic; + signal char0_dat_o: std_logic_vector(7 downto 0); + signal char1_dat_o: std_logic_vector(7 downto 0); + signal char2_dat_o: std_logic_vector(7 downto 0); + signal char3_dat_o: std_logic_vector(7 downto 0); + signal tile_dat_o: std_logic_vector(7 downto 0); + + -- BRAM is faster than the external ram, and is dual-ported to prevent bus + -- contention with whatever system is attached to the tiler. BRAM is also + -- synchronous, which means it will take multiple clock cycles to process + -- each pixel - so a cheeky lil pipeline is needed. Also, the BRAM cells + -- take separate clocks for each interface, which allows the VGA controller + -- to be driven at a different clock rate than the rest of the system, which + -- allows for new and exciting screen resolutions. + + -- BRAM access is kept in its own separate stages for convenience. Other + -- stages can be further split if higher performance is necessary. The + -- output registers of the BRAM cells are treated as part of their + -- respective interstage registers. Some signals (like all the bits of + -- pix_x and pix_y) are passed down the pipe even after they're strictly + -- necessary. This is for convenience if more is added later, and they will + -- be trimmed by the specializer during synthesis. + + -- Stage 0: Compute character location within screen buffer + signal s0_h_sync: std_logic; + signal s0_v_sync: std_logic; + signal s0_h_blank: std_logic; + signal s0_v_blank: std_logic; + signal s0_pix_x: std_logic_vector(10 downto 0); + signal s0_pix_y: std_logic_vector(9 downto 0); + signal s0_char_row: unsigned(6 downto 0); + signal s0_char_col: unsigned(6 downto 0); + signal s0_char_idx: unsigned(12 downto 0); + + -- Register 0-1 + signal s01_h_sync_reg: std_logic; + signal s01_v_sync_reg: std_logic; + signal s01_h_blank_reg: std_logic; + signal s01_v_blank_reg: std_logic; + signal s01_pix_x_reg: std_logic_vector(10 downto 0); + signal s01_pix_y_reg: std_logic_vector(9 downto 0); + signal s01_char_idx_reg: unsigned(12 downto 0); + + -- Stage 1: Lookup screen character's tile ID within screen buffer + + -- Register 1-2 + signal s12_h_sync_reg: std_logic; + signal s12_v_sync_reg: std_logic; + signal s12_h_blank_reg: std_logic; + signal s12_v_blank_reg: std_logic; + signal s12_pix_x_reg: std_logic_vector(10 downto 0); + signal s12_pix_y_reg: std_logic_vector(9 downto 0); + signal s12_char_idx_reg: unsigned(12 downto 0); + signal s12_bank0_char_reg: std_logic_vector(7 downto 0); + signal s12_bank1_char_reg: std_logic_vector(7 downto 0); + signal s12_bank2_char_reg: std_logic_vector(7 downto 0); + signal s12_bank3_char_reg: std_logic_vector(7 downto 0); + + -- Stage 2: Select the tile ID from the correct bank and compute tile location within tile ram + signal s2_char: std_logic_vector(7 downto 0); + signal s2_tile_idx: std_logic_vector(10 downto 0); + + -- Register 2-3 + signal s23_h_sync_reg: std_logic; + signal s23_v_sync_reg: std_logic; + signal s23_h_blank_reg: std_logic; + signal s23_v_blank_reg: std_logic; + signal s23_pix_x_reg: std_logic_vector(10 downto 0); + signal s23_pix_y_reg: std_logic_vector(9 downto 0); + signal s23_tile_idx_reg: std_logic_vector(10 downto 0); + + -- Stage 3: Lookup tile slice within tile ram + + -- Register 3-4 + signal s34_h_sync_reg: std_logic; + signal s34_v_sync_reg: std_logic; + signal s34_h_blank_reg: std_logic; + signal s34_v_blank_reg: std_logic; + signal s34_pix_x_reg: std_logic_vector(10 downto 0); + signal s34_pix_y_reg: std_logic_vector(9 downto 0); + signal s34_tile_line_reg: std_logic_vector(7 downto 0); + + -- Stage 4: Select bit within tile slice an map to color + signal s4_bit: std_logic; + signal s4_visible: std_logic; + signal s4_vga_red: std_logic_vector(3 downto 1); + signal s4_vga_green: std_logic_vector(3 downto 1); + signal s4_vga_blue: std_logic_vector(3 downto 2); + + -- Register 4-output + signal s4o_h_sync_reg: std_logic; + signal s4o_v_sync_reg: std_logic; + signal s4o_vga_red_reg: std_logic_vector(3 downto 1); + signal s4o_vga_green_reg: std_logic_vector(3 downto 1); + signal s4o_vga_blue_reg: std_logic_vector(3 downto 2); + +begin + + ---------------------------------------------------------------------------- + -- System Wishbone interface + -- FIXME: this may do double reads/writes - not a problem for now though + + + process (rst_i, clk_i, wb_state_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + wb_state_reg <= S_IDLE; + else + wb_state_reg <= wb_state_next; + end if; + end if; + end process; + + process (wb_state_reg, stb_i) + begin + wb_state_next <= wb_state_reg; + ack_o <= '0'; + + case wb_state_reg is + when S_IDLE => + if stb_i = '1' then + wb_state_next <= S_ACK; + end if; + + when S_ACK => + ack_o <= '1'; + wb_state_next <= S_IDLE; + + when others => + wb_state_next <= S_IDLE; + end case; + end process; + + with adr_i(13 downto 11) select dat_o <= + char0_dat_o when "000", + char1_dat_o when "001", + char2_dat_o when "010", + char3_dat_o when "011", + tile_dat_o when "100", + (others => '1') when others; + + char0_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "000" else '0'; + char1_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "001" else '0'; + char2_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "010" else '0'; + char3_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "011" else '0'; + tile_stb_i <= stb_i and cyc_i when adr_i(13 downto 11) = "100" else '0'; + + + ---------------------------------------------------------------------------- + -- (Stage -1: VGA pixel counter and signal generation) + + e_vga_counter: entity work.vga_counter + port map ( + clk_50 => clk_i, + + pix_x => s0_pix_x, + pix_y => s0_pix_y, + + h_blank => s0_h_blank, + v_blank => s0_v_blank, + + h_sync => s0_h_sync, + v_sync => s0_v_sync + ); + + + ---------------------------------------------------------------------------- + -- Stage 0: Compute character location within screen buffer + + -- Divide pixel positions by 8 to get row and column + s0_char_col <= unsigned(s0_pix_x(10 downto 4)); + s0_char_row <= unsigned(s0_pix_y(9 downto 3)); + + -- Index into screen character buffer is row * 80 + col + -- Shouldn't carry for pix_x and pix_y within visible screen range, 13 bits is enough + -- Used for reads only, so nondestructive if out of visible screen range + s0_char_idx <= ((("00" & s0_char_row) + (s0_char_row & "00")) & "0000") + s0_char_col; + + + -- Register 0-1 + process (clk_i, s0_h_sync, s0_v_sync, s0_h_blank, s0_v_blank, s0_pix_x, s0_pix_y, s0_char_idx) + begin + if rising_edge(clk_i) then + s01_h_sync_reg <= s0_h_sync; + s01_v_sync_reg <= s0_v_sync; + s01_h_blank_reg <= s0_h_blank; + s01_v_blank_reg <= s0_v_blank; + s01_pix_x_reg <= s0_pix_x; + s01_pix_y_reg <= s0_pix_y; + s01_char_idx_reg <= s0_char_idx; + end if; + end process; + + + ---------------------------------------------------------------------------- + -- Stage 1: Look up screen character's tile ID within screen buffer + + + e_char_bank_0: ramb16_s9_s9 + generic map ( + INIT_00 => x"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100", + INIT_01 => x"3f3e3d3c3b3a393837363534333231302f2e2d2c2b2a29282726252423222120", + INIT_02 => x"5f5e5d5c5b5a595857565554535251504f4e4d4c4b4a49484746454443424140", + INIT_03 => x"7f7e7d7c7b7a797877767574737271706f6e6d6c6b6a69686766656463626160", + INIT_04 => x"9f9e9d9c9b9a999897969594939291908f8e8d8c8b8a89888786858483828180", + INIT_05 => x"bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0afaeadacabaaa9a8a7a6a5a4a3a2a1a0", + INIT_06 => x"dfdedddcdbdad9d8d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0", + INIT_07 => x"fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0" + ) + port map ( + -- Port A, tiler access + ssra => rst_i, + clka => clk_i, + ena => '1', + wea => '0', + addra => std_logic_vector(s01_char_idx_reg(10 downto 0)), + + doa => s12_bank0_char_reg, + dopa => open, + dia => (others => '0'), + dipa => "0", + + -- Port B, system access + ssrb => rst_i, + clkb => clk_i, + enb => char0_stb_i, + web => we_i, + addrb => adr_i(10 downto 0), + + dob => char0_dat_o, + dopb => open, + dib => dat_i, + dipb => "0" + ); + + e_char_bank_1: ramb16_s9_s9 + port map ( + -- Port A, tiler access + ssra => rst_i, + clka => clk_i, + ena => '1', + wea => '0', + addra => std_logic_vector(s01_char_idx_reg(10 downto 0)), + + doa => s12_bank1_char_reg, + dopa => open, + dia => (others => '0'), + dipa => "0", + + -- Port B, system access + ssrb => rst_i, + clkb => clk_i, + enb => char1_stb_i, + web => we_i, + addrb => adr_i(10 downto 0), + + dob => char1_dat_o, + dopb => open, + dib => dat_i, + dipb => "0" + ); + + e_char_bank_2: ramb16_s9_s9 + port map ( + -- Port A, tiler access + ssra => rst_i, + clka => clk_i, + ena => '1', + wea => '0', + addra => std_logic_vector(s01_char_idx_reg(10 downto 0)), + + doa => s12_bank2_char_reg, + dopa => open, + dia => (others => '0'), + dipa => "0", + + -- Port B, system access + ssrb => rst_i, + clkb => clk_i, + enb => char2_stb_i, + web => we_i, + addrb => adr_i(10 downto 0), + + dob => char2_dat_o, + dopb => open, + dib => dat_i, + dipb => "0" + ); + + e_char_bank_3: ramb16_s9_s9 + port map ( + -- Port A, tiler access + ssra => rst_i, + clka => clk_i, + ena => '1', + wea => '0', + addra => std_logic_vector(s01_char_idx_reg(10 downto 0)), + + doa => s12_bank3_char_reg, + dopa => open, + dia => (others => '0'), + dipa => "0", + + -- Port B, system access + ssrb => rst_i, + clkb => clk_i, + enb => char3_stb_i, + web => we_i, + addrb => adr_i(10 downto 0), + + dob => char3_dat_o, + dopb => open, + dib => dat_i, + dipb => "0" + ); + + + -- Register 1-2 + process (clk_i, s01_h_sync_reg, s01_v_sync_reg, s01_h_blank_reg, s01_v_blank_reg, s01_pix_x_reg, s01_pix_y_reg, s01_char_idx_reg) + begin + if rising_edge(clk_i) then + s12_h_sync_reg <= s01_h_sync_reg; + s12_v_sync_reg <= s01_v_sync_reg; + s12_h_blank_reg <= s01_h_blank_reg; + s12_v_blank_reg <= s01_v_blank_reg; + s12_pix_x_reg <= s01_pix_x_reg; + s12_pix_y_reg <= s01_pix_y_reg; + s12_char_idx_reg <= s01_char_idx_reg; + -- s12_bank0_char_reg + -- s12_bank1_char_reg + -- s12_bank2_char_reg + -- s12_bank3_char_reg + end if; + end process; + + + ---------------------------------------------------------------------------- + -- Stage 2: Select the tile ID from the correct bank and compute tile location within tile ram + + + with s12_char_idx_reg(12 downto 11) select s2_char <= + s12_bank0_char_reg when "00", + s12_bank1_char_reg when "01", + s12_bank2_char_reg when "10", + s12_bank3_char_reg when "11", + (others => 'X') when others; + + s2_tile_idx <= s2_char & s12_pix_y_reg(2 downto 0); + + + -- Register 2-3 + process (clk_i, s12_h_sync_reg, s12_v_sync_reg, s12_h_blank_reg, s12_v_blank_reg, s12_pix_x_reg, s12_pix_y_reg, s2_tile_idx) + begin + if rising_edge(clk_i) then + s23_h_sync_reg <= s12_h_sync_reg; + s23_v_sync_reg <= s12_v_sync_reg; + s23_h_blank_reg <= s12_h_blank_reg; + s23_v_blank_reg <= s12_v_blank_reg; + s23_pix_x_reg <= s12_pix_x_reg; + s23_pix_y_reg <= s12_pix_y_reg; + s23_tile_idx_reg <= s2_tile_idx; + end if; + end process; + + + ---------------------------------------------------------------------------- + -- Stage 3: Lookup tile slice within tile ram + + + e_tile_ram: ramb16_s9_s9 + generic map ( + INIT_08 => x"006666ff66ff6666000000000066666600180000181818180000000000000000", + INIT_09 => x"0000000000180c06003f6667383c663c00466630180c666200187c063c603e18", + INIT_0A => x"000018187e1818000000663cff3c66000030180c0c0c1830000c18303030180c", + INIT_0B => x"006030180c0603000018180000000000000000007e0000003018180000000000", + INIT_0C => x"003c66061c06663c007e60300c06663c007e181818381818003c6666766e663c", + INIT_0D => x"00181818180c667e003c66667c60663c003c6606067c607e0006067f661e0e06", + INIT_0E => x"30181800001800000000180000180000003c66063e66663c003c66663c66663c", + INIT_0F => x"001800180c06663c0070180c060c18700000007e007e0000000e18306030180e", + INIT_10 => x"003c66606060663c007c66667c66667c006666667e663c18003c62606e6e663c", + INIT_11 => x"003c66666e60663c006060607860607e007e60607860607e00786c6666666c78", + INIT_12 => x"00666c7870786c6600386c0c0c0c0c1e003c18181818183c006666667e666666", + INIT_13 => x"003c66666666663c0066666e7e7e7666006363636b7f7763007e606060606060", + INIT_14 => x"003c66063c60663c00666c787c66667c000e3c666666663c006060607c66667c", + INIT_15 => x"0063777f6b63636300183c6666666666003c666666666666001818181818187e", + INIT_16 => x"003c30303030303c007e6030180c067e001818183c6666660066663c183c6666", + INIT_17 => x"00000000000000000000000000000000003c0c0c0c0c0c3c0000000000000000", + INIT_18 => x"003c6060603c0000007c66667c606000003e663e063c00000000000000000000", + INIT_19 => x"7c063e66663e0000001818183e180e00003c607e663c0000003e66663e060600", + INIT_1A => x"00666c786c6060003c06060606000600003c181838001800006666667c606000", + INIT_1B => x"003c6666663c000000666666667c000000636b7f7f660000003c181818183800", + INIT_1C => x"007c063c603e000000606060667c000006063e66663e000060607c66667c0000", + INIT_1D => x"00363e7f6b63000000183c6666660000003e666666660000000e1818187e1800", + INIT_1E => x"0000000000000000007e30180c7e0000780c3e666666000000663c183c660000" + ) + port map ( + -- Port A, tiler access + ssra => rst_i, + clka => clk_i, + ena => '1', + wea => '0', + addra => s23_tile_idx_reg, + + doa => s34_tile_line_reg, + dopa => open, + dia => (others => '0'), + dipa => "0", + + -- Port B, system access + ssrb => rst_i, + clkb => clk_i, + enb => tile_stb_i, + web => we_i, + addrb => adr_i(10 downto 0), + + dob => tile_dat_o, + dopb => open, + dib => dat_i, + dipb => "0" + ); + + + -- Register 3-4 + process (clk_i, s23_h_sync_reg, s23_v_sync_reg, s23_h_blank_reg, s23_v_blank_reg, s23_pix_x_reg, s23_pix_y_reg) + begin + if rising_edge(clk_i) then + s34_h_sync_reg <= s23_h_sync_reg; + s34_v_sync_reg <= s23_v_sync_reg; + s34_h_blank_reg <= s23_h_blank_reg; + s34_v_blank_reg <= s23_v_blank_reg; + s34_pix_x_reg <= s23_pix_x_reg; + s34_pix_y_reg <= s23_pix_y_reg; + -- s34_tile_line_reg + end if; + end process; + + + ---------------------------------------------------------------------------- + -- Stage 4: Select bit within tile slice an map to color + + + --with s34_pix_x_reg(3 downto 1) select s4_bit <= + -- s34_tile_line_reg(7) when "000", + -- s34_tile_line_reg(6) when "001", + -- s34_tile_line_reg(5) when "010", + -- s34_tile_line_reg(4) when "011", + -- s34_tile_line_reg(3) when "100", + -- s34_tile_line_reg(2) when "101", + -- s34_tile_line_reg(1) when "110", + -- s34_tile_line_reg(0) when others; + + s4_bit <= s34_tile_line_reg(to_integer(unsigned(not s34_pix_x_reg(3 downto 1)))); + s4_visible <= not (s34_h_blank_reg or s34_v_blank_reg); + + s4_vga_red <= "111" when (s4_visible and s4_bit) = '1' else "000"; + s4_vga_green <= "111" when (s4_visible and s4_bit) = '1' else "000"; + s4_vga_blue <= "11" when (s4_visible and s4_bit) = '1' else "00"; + + + -- Register 4-output + process (clk_i, s34_h_sync_reg, s34_v_sync_reg, s4_vga_red, s4_vga_green, s4_vga_blue) + begin + if rising_edge(clk_i) then + s4o_h_sync_reg <= s34_h_sync_reg; + s4o_v_sync_reg <= s34_v_sync_reg; + s4o_vga_red_reg <= s4_vga_red; + s4o_vga_green_reg <= s4_vga_green; + s4o_vga_blue_reg <= s4_vga_blue; + end if; + end process; + + + ---------------------------------------------------------------------------- + -- Output + + + Hsync <= s4o_h_sync_reg; + Vsync <= s4o_v_sync_reg; + vgaRed <= s4o_vga_red_reg; + vgaGreen <= s4o_vga_green_reg; + vgaBlue <= s4o_vga_blue_reg; + +end behavioral; diff --git a/projects/cpu_0/asm/as.py b/projects/cpu_0/asm/as.py new file mode 100755 index 0000000..8db13b8 --- /dev/null +++ b/projects/cpu_0/asm/as.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 + +import argparse +import struct + + +# ------------------------------------------------------------------------------ + + +def assemble(source: str) -> bytearray: + b = bytearray() + syms = dict() + forwards = list() + tokens = source.split() + instrs = { + 'nop': b'\x00', + '#8': b'\x01', '#32': b'\x02', + '@8': b'\x03', '@32': b'\x04', + '!8': b'\x05', '!32': b'\x06', + 'r@': b'\x07', '>r': b'\x08', 'r>': b'\x09', + 'call': b'\x0a', ';': b'\x0b', + 'jmp': b'\x0c', 'jz': b'\x0d', 'jn': b'\x0e', 'jp': b'\x0f', + '+': b'\x10', '-': b'\x11', + '&': b'\x12', '|': b'\x13', '^': b'\x14', '~': b'\x15', + 'dup': b'\x16', 'drop': b'\x17', 'swap': b'\x18', + 'ien': b'\x19', 'idis': b'\x1a', + } + + comment = False + for token in tokens: + token = token.lower() + + if comment: + if token == ')': + comment = False + continue + + if token == '(': + comment = True + elif token in instrs: + b += instrs[token] + elif '=' in token: + sym, value = token.split('=', maxsplit=1) + value = int(value, 0) + syms[sym] = value + elif token.endswith(':'): + assert token[0] in '_abcdefghijklmnopqrstuvwxyz' + syms[token[:-1]] = len(b) + elif token[0] in '-0123456789': + if token.endswith('i8'): + val = int(token[:-2], 0) + assert 0 <= val <= 255 + b += bytes([val]) + elif token.endswith('i32'): + val = int(token[:-3], 0) + assert -0x80000000 <= val <= 0xffffffff + if val < 0: + b += struct.pack(' int: + parser = argparse.ArgumentParser('AS - CPU0 Assembler') + parser.add_argument('--outfile', '-o', help='Output filename') + parser.add_argument('filename', help='Source filename') + args = parser.parse_args() + + with open(args.filename, 'r') as f: + with open(args.outfile, 'wb') as g: + s = f.read() + b = assemble(s) + g.write(b) + + return 0 + + +if __name__ == '__main__': + exit(main()) diff --git a/projects/cpu_0/asm/hellorld.asm b/projects/cpu_0/asm/hellorld.asm new file mode 100644 index 0000000..70eba67 --- /dev/null +++ b/projects/cpu_0/asm/hellorld.asm @@ -0,0 +1,31 @@ +( host registers ) +host_ctrl=0x02004000 +host_flags=0x02004001 +host_mbox=0x02004002 +host_swled=0x02004003 +host_sseg0=0x02004004 +host_sseg1=0x02004005 +host_sseg2=0x02004006 +host_sseg3=0x02004007 + +( ps2 registers ) +ps2_ctrl=0x02004008 +ps2_imask=0x02004009 +ps2_iflag=0x0200400a +ps2_error=0x0200400b +ps2_data=0x0200400c + +( rs232 registers ) +rs232_ctrl=0x02004010 +rs232_baudl=0x02004011 +rs232_baudh=0x02004012 +rs232_imask=0x02004013 +rs232_iflag=0x02004014 +rs232_data=0x02004015 + +( ivec -- ) +isr: + +halt: + #8 0xa5i8 drop + jmp halt diff --git a/projects/cpu_0/asm/test.asm b/projects/cpu_0/asm/test.asm new file mode 100644 index 0000000..7470ba5 --- /dev/null +++ b/projects/cpu_0/asm/test.asm @@ -0,0 +1,121 @@ +( host registers ) +host_ctrl=0x02004000 +host_flags=0x02004001 +host_mbox=0x02004002 +host_swled=0x02004003 +host_sseg0=0x02004004 +host_sseg1=0x02004005 +host_sseg2=0x02004006 +host_sseg3=0x02004007 + +( ps2 registers ) +ps2_ctrl=0x02004008 +ps2_imask=0x02004009 +ps2_iflag=0x0200400a +ps2_error=0x0200400b +ps2_data=0x0200400c + +( rs232 registers ) +rs232_ctrl=0x02004010 +rs232_baudl=0x02004011 +rs232_baudh=0x02004012 +rs232_imask=0x02004013 +rs232_iflag=0x02004014 +rs232_data=0x02004015 + +( ivec -- ) +isr: + ( if ivec > 15 goto isr_unknown ) + dup #8 15i8 - jn isr_unknown + + ( jmp [isr_jumptable + ivec*4] ) + dup + dup + #32 isr_jmptable + @32 >r ; + +isr_jmptable: + isr_0 isr_1 isr_2 isr_3 + isr_4 isr_5 isr_6 isr_7 + isr_8 isr_9 isr_a isr_b + isr_c isr_d isr_e isr_f + +isr_unknown: + drop ien ; + +( reset ) +isr_0: + ( write something to seven seg display ) + #8 0x3ci8 #32 host_sseg0 !8 + #8 0xa5i8 #32 host_sseg1 !8 + #8 0x5ai8 #32 host_sseg2 !8 + #8 0xc3i8 #32 host_sseg3 !8 + + ( write 0x1458 to baud register - 9600 baud ) + #8 0x14i8 #32 rs232_baudl !8 + #8 0x58i8 #32 rs232_baudh !8 + + ( enable rx ready interrupt ) + #8 0x01i8 #32 rs232_imask !8 + + ( enable transmitter and receiver ) + #8 0x03i8 #32 rs232_ctrl !8 + + ( Say hi ) + #8 0x41i8 #32 rs232_data !8 + + jmp halt + +( host mailbox flag interrupt ) +isr_1: + ien ; + +( ps2 interrupt ) +isr_2: + ien ; + +( uart interrupt ) +isr_3: + ( if [rs232_iflag] & 0x01 == 0x00 then return ) + #32 rs232_iflag @8 #8 0x01i8 & jz _isr_3_ret + + ( [rs232_data] = [rs232_data] ) + #32 rs232_data dup @8 !8 +_isr_3_ret: + ien ; + +isr_4: + ien ; + +isr_5: + ien ; + +isr_6: + ien ; + +isr_7: + ien ; + +isr_8: + ien ; + +isr_9: + ien ; + +isr_a: + ien ; + +isr_b: + ien ; + +isr_c: + ien ; + +isr_d: + ien ; + +isr_e: + ien ; + +isr_f: + ien ; + +halt: + jmp halt diff --git a/projects/cpu_0/cpu.vhd b/projects/cpu_0/cpu.vhd new file mode 100644 index 0000000..e283b24 --- /dev/null +++ b/projects/cpu_0/cpu.vhd @@ -0,0 +1,943 @@ +-------------------------------------------------------------------------------- +-- +-- +---+ +---+ +---+ +---+ +---+ +-- | T | | R | |MDR| | I | |PC | +-- +---+ +---+ +---+ +---+ +---+ +-- | N | : : +-- +---+ +-- : : +-- +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity cpu is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + cyc_o: out std_logic; + stb_o: out std_logic; + we_o: out std_logic; + ack_i: in std_logic; + adr_o: out std_logic_vector(31 downto 0); + dat_o: out std_logic_vector(7 downto 0); + dat_i: in std_logic_vector(7 downto 0); + + int_i: in std_logic; + vec_i: in std_logic_vector(7 downto 0); + + debug_i: in std_logic_vector(63 downto 0); + debug_o: out std_logic_vector(63 downto 0) + ); +end cpu; + + +architecture behavioral of cpu is + + type state_t is ( + S_WAIT, S_FETCH_2, + S_RESET, S_FETCH, S_MEMORY, + S_IMM8, S_IMM32_0, S_IMM32_1, S_IMM32_2, S_IMM32_3, + S_LD8, S_LD32_0, S_LD32_1, S_LD32_2, S_LD32_3, + S_ST8, S_ST32_0, S_ST32_1, S_ST32_2, S_ST32_3, + S_EXEC, S_INT + ); + + type mem_op_t is (MEM_IMM8, MEM_IMM32, MEM_LD8, MEM_LD32, MEM_ST8, MEM_ST32, MEM_NONE); + type adr_sel_t is (ADR_PC, ADR_T, ADR_T0, ADR_T1, ADR_T2, ADR_T3); + type dat_sel_t is (DAT_N0, DAT_N1, DAT_N2, DAT_N3); + type alu_op_t is (ALU_T, ALU_N, ALU_ADD, ALU_SUB, ALU_AND, ALU_OR, ALU_XOR, ALU_NOT, ALU_R, ALU_MDR, ALU_INTVEC); + type r_sel_t is (R_T, R_PC, R_NC); + type pc_sel_t is (PC_MDR, PC_R, PC_INC, PC_NC, PC_INTVEC); + type stackop_t is (ST_INC, ST_DEC, ST_NC); + type cond_t is (C_NEG, C_ZERO, C_POS, C_NONE); + + type instr_decode_t is record + -- Memory operation + mem_op: mem_op_t; + + -- Stack updates + s_op: stackop_t; + r_op: stackop_t; + + -- Datapath multiplexion + alu_op: alu_op_t; -- selects new T + t_to_n: std_logic; -- T->N (push) + r_sel: r_sel_t; -- selects new R + pc_sel: pc_sel_t; -- selects new PC + + imask_set: std_logic; + imask_clr: std_logic; + cond: cond_t; + end record; + + -- Control FSM + signal state_reg: state_t; + signal state_next: state_t; + signal adr_sel: adr_sel_t; + + -- Instruction register + signal ins_reg: std_logic_vector(7 downto 0); + signal ins_ld: std_logic; + signal ins_decode: instr_decode_t; + signal dat_sel: dat_sel_t; + + -- Program counter + signal pc_reg: std_logic_vector(31 downto 0); + signal pc_next: std_logic_vector(31 downto 0); + signal pc_sel: pc_sel_t; + + -- Memory data register + signal mdr_reg: std_logic_vector(31 downto 0); + signal mdr_ld_0: std_logic; + signal mdr_ld_1: std_logic; + signal mdr_ld_2: std_logic; + signal mdr_ld_3: std_logic; + + -- Parameter stack + signal s_ptr_reg: unsigned(7 downto 0); + signal s_ptr_next: unsigned(7 downto 0); + signal s_ptr_op: stackop_t; + signal s_ptr_ld: std_logic; + signal s_write: std_logic; + signal s_n_reg: std_logic_vector(31 downto 0); + signal s_t_reg: std_logic_vector(31 downto 0); + signal s_t_new: std_logic_vector(31 downto 0); + signal s_t_ld: std_logic; + + -- Return stack + signal r_ptr_reg: unsigned(7 downto 0); + signal r_ptr_next: unsigned(7 downto 0); + signal r_ptr_op: stackop_t; + signal r_ptr_ld: std_logic; + signal r_write: std_logic; + signal r_tos_reg: std_logic_vector(31 downto 0); + signal r_tos_new: std_logic_vector(31 downto 0); + + signal addra: std_logic_vector(8 downto 0); + signal addrb: std_logic_vector(8 downto 0); + + -- Kludged-in to allow S_INT logic to override the values from ins_decode + signal r_sel: r_sel_t; + signal alu_op: alu_op_t; + signal imask_reg: std_logic; + signal imask_set: std_logic; + signal imask_clr: std_logic; + + signal n: std_logic; + signal z: std_logic; + signal p: std_logic; + signal ins_pc_sel: pc_sel_t; + + signal debug_reg: std_logic_vector(15 downto 0); + +begin + + -- Control state machine + process (rst_i, clk_i, state_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + state_reg <= S_RESET; + else + state_reg <= state_next; + end if; + end if; + end process; + + debug_o(63 downto 32) <= pc_reg; + debug_o(15 downto 8) <= dat_i; + debug_o(23 downto 16) <= mdr_reg(7 downto 0); + debug_o( 7 downto 0) <= ins_reg; + + process (state_reg, ack_i, ins_decode, int_i, ins_pc_sel, imask_reg, debug_i) + begin + state_next <= state_reg; + cyc_o <= '0'; + stb_o <= '0'; + we_o <= '0'; + adr_sel <= ADR_PC; + ins_ld <= '0'; + pc_sel <= PC_NC; + mdr_ld_0 <= '0'; + mdr_ld_1 <= '0'; + mdr_ld_2 <= '0'; + mdr_ld_3 <= '0'; + dat_sel <= DAT_N0; + s_t_ld <= '0'; + s_write <= '0'; + r_write <= '0'; + s_ptr_op <= ST_NC; + r_ptr_op <= ST_NC; + s_ptr_ld <= '0'; + r_ptr_ld <= '0'; + r_sel <= ins_decode.r_sel; + alu_op <= ins_decode.alu_op; + imask_set <= '0'; + imask_clr <= '0'; + + case state_reg is + when S_FETCH => + if debug_i(0) = '1' then + state_next <= S_WAIT; + end if; + + when S_WAIT => + if debug_i(0) = '0' then + state_next <= S_FETCH_2; + end if; + + when S_FETCH_2 => + adr_sel <= ADR_PC; + cyc_o <= '1'; + stb_o <= '1'; + ins_ld <= '1'; + + if ack_i = '1' then + pc_sel <= PC_INC; + state_next <= S_MEMORY; + end if; + + when S_MEMORY => + -- Mask interrupts now if the instruction requests it + -- The mask flag register will be set in the following clock cycle, + -- so this prevents us from having to check ins_decode.imask_set + -- in the S_EXEC state, which would otherwise be necessary to make + -- sure we don't enter an interrupt after a "mask-int" instruction + -- and then return with interrupts unmasked into a critical section + imask_set <= ins_decode.imask_set; + + case ins_decode.mem_op is + when MEM_IMM8 => state_next <= S_IMM8; + when MEM_IMM32 => state_next <= S_IMM32_0; + when MEM_LD8 => state_next <= S_LD8; + when MEM_LD32 => state_next <= S_LD32_0; + when MEM_ST8 => state_next <= S_ST8; + when MEM_ST32 => state_next <= S_ST32_0; + when others => state_next <= S_EXEC; + end case; + + when S_IMM8 => + adr_sel <= ADR_PC; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_0 <= '1'; + + if ack_i = '1' then + pc_sel <= PC_INC; + state_next <= S_EXEC; + end if; + + when S_IMM32_0 => + adr_sel <= ADR_PC; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_0 <= '1'; + + if ack_i = '1' then + pc_sel <= PC_INC; + state_next <= S_IMM32_1; + end if; + + when S_IMM32_1 => + adr_sel <= ADR_PC; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_1 <= '1'; + + if ack_i = '1' then + pc_sel <= PC_INC; + state_next <= S_IMM32_2; + end if; + + when S_IMM32_2 => + adr_sel <= ADR_PC; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_2 <= '1'; + + if ack_i = '1' then + pc_sel <= PC_INC; + state_next <= S_IMM32_3; + end if; + + when S_IMM32_3 => + adr_sel <= ADR_PC; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_3 <= '1'; + + if ack_i = '1' then + pc_sel <= PC_INC; + state_next <= S_EXEC; + end if; + + when S_LD8 => + adr_sel <= ADR_T; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_0 <= '1'; + + if ack_i = '1' then + state_next <= S_EXEC; + end if; + + when S_LD32_0 => + adr_sel <= ADR_T0; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_0 <= '1'; + + if ack_i = '1' then + state_next <= S_LD32_1; + end if; + + when S_LD32_1 => + adr_sel <= ADR_T1; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_1 <= '1'; + + if ack_i = '1' then + state_next <= S_LD32_2; + end if; + + when S_LD32_2 => + adr_sel <= ADR_T2; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_2 <= '1'; + + if ack_i = '1' then + state_next <= S_LD32_3; + end if; + + when S_LD32_3 => + adr_sel <= ADR_T3; + cyc_o <= '1'; + stb_o <= '1'; + mdr_ld_3 <= '1'; + + if ack_i = '1' then + state_next <= S_EXEC; + end if; + + when S_ST8 => + adr_sel <= ADR_T; + cyc_o <= '1'; + stb_o <= '1'; + we_o <= '1'; + dat_sel <= DAT_N0; + + if ack_i = '1' then + state_next <= S_EXEC; + end if; + + when S_ST32_0 => + adr_sel <= ADR_T0; + cyc_o <= '1'; + stb_o <= '1'; + we_o <= '1'; + dat_sel <= DAT_N0; + + if ack_i = '1' then + state_next <= S_ST32_1; + end if; + + when S_ST32_1 => + adr_sel <= ADR_T1; + cyc_o <= '1'; + stb_o <= '1'; + we_o <= '1'; + dat_sel <= DAT_N1; + + if ack_i = '1' then + state_next <= S_ST32_2; + end if; + + when S_ST32_2 => + adr_sel <= ADR_T2; + cyc_o <= '1'; + stb_o <= '1'; + we_o <= '1'; + dat_sel <= DAT_N2; + + if ack_i = '1' then + state_next <= S_ST32_3; + end if; + + when S_ST32_3 => + adr_sel <= ADR_T3; + cyc_o <= '1'; + stb_o <= '1'; + we_o <= '1'; + dat_sel <= DAT_N3; + + if ack_i = '1' then + state_next <= S_EXEC; + end if; + + when S_EXEC => + -- At this point, MDR will contain any immediate or loaded value and any stores have been performed + + -- Load T with new value + s_t_ld <= '1'; + + -- Load N with new value + if ins_decode.t_to_n = '1' then + s_write <= '1'; + end if; + + -- Load R with new value + if ins_decode.r_sel /= R_NC then + r_write <= '1'; + end if; + + -- Load PC with new value + -- ins_pc_sel includes conditions + pc_sel <= ins_pc_sel; + + -- Use updated stack pointers + s_ptr_op <= ins_decode.s_op; + r_ptr_op <= ins_decode.r_op; + s_ptr_ld <= '1'; + r_ptr_ld <= '1'; + + -- Clear the interrupt mask flag if necessary + -- The flag will be cleared in the next clock cycle, so the next + -- instruction is guaranteed to be executed if an interrupt is + -- pending, useful for clearing the interrupt flag and then returning + imask_clr <= ins_decode.imask_clr; + + -- Check for interrupts during the execute step so we don't have any in-progress memory operations to cancel + if int_i = '1' and imask_reg = '0' then + state_next <= S_INT; + else + state_next <= S_FETCH; + end if; + + when S_INT => + -- Load T with the vector value, load N with old T, push down the stack + alu_op <= ALU_INTVEC; + s_t_ld <= '1'; + s_write <= '1'; + s_ptr_op <= ST_INC; + s_ptr_ld <= '1'; + + -- Push PC onto the return stack + r_sel <= R_PC; + r_write <= '1'; + r_ptr_op <= ST_INC; + r_ptr_ld <= '1'; + + -- Load PC with the vector address + pc_sel <= PC_INTVEC; + + -- Mask further interruptions + imask_set <= '1'; + + state_next <= S_FETCH; + + when S_RESET => + state_next <= S_FETCH; + + when others => + state_next <= S_FETCH; + end case; + end process; + + + -- Interrupt mask flag + process (rst_i, clk_i, imask_set, imask_clr) + begin + if rising_edge(clk_i) then + if rst_i = '1' or imask_clr = '1' then + imask_reg <= '0'; + elsif imask_set = '1' then + imask_reg <= '1'; + end if; + end if; + end process; + + + -- Program counter + process (n, z, p, ins_decode) + begin + if ins_decode.cond = C_NONE then + ins_pc_sel <= ins_decode.pc_sel; + elsif ins_decode.cond = C_NEG then + if n = '1' then + ins_pc_sel <= ins_decode.pc_sel; + else + ins_pc_sel <= PC_NC; + end if; + elsif ins_decode.cond = C_ZERO then + if z = '1' then + ins_pc_sel <= ins_decode.pc_sel; + else + ins_pc_sel <= PC_NC; + end if; + elsif ins_decode.cond = C_POS then + if p = '1' then + ins_pc_sel <= ins_decode.pc_sel; + else + ins_pc_sel <= PC_NC; + end if; + end if; + end process; + + with pc_sel select pc_next <= + mdr_reg when PC_MDR, + r_tos_reg when PC_R, + std_logic_vector(unsigned(pc_reg) + 1) when PC_INC, + (others => '0') when PC_INTVEC, + pc_reg when others; + + process (rst_i, clk_i, pc_reg, pc_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + pc_reg <= (others => '0'); + else + pc_reg <= pc_next; + end if; + end if; + end process; + + + -- Instruction register and decode + process (clk_i, ins_ld, dat_i) + begin + if rising_edge(clk_i) then + if ins_ld = '1' then + ins_reg <= dat_i; + end if; + end if; + end process; + + with ins_reg select ins_decode <= + ( -- #8 + mem_op => MEM_IMM8, + s_op => ST_INC, + r_op => ST_NC, + alu_op => ALU_MDR, t_to_n => '1', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00000001", + ( -- #32 + mem_op => MEM_IMM32, + s_op => ST_INC, + r_op => ST_NC, + alu_op => ALU_MDR, t_to_n => '1', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00000010", + ( -- @8 + mem_op => MEM_LD8, + s_op => ST_NC, + r_op => ST_NC, + alu_op => ALU_MDR, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00000011", + ( -- @32 + mem_op => MEM_LD32, + s_op => ST_NC, + r_op => ST_NC, + alu_op => ALU_MDR, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00000100", + ( -- !8 + mem_op => MEM_ST8, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_N, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00000101", + ( -- !32 + mem_op => MEM_ST32, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_N, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00000110", + ( -- r@ + mem_op => MEM_NONE, + s_op => ST_INC, + r_op => ST_NC, + alu_op => ALU_R, t_to_n => '1', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00000111", + ( -- >r + mem_op => MEM_NONE, + s_op => ST_DEC, + r_op => ST_INC, + alu_op => ALU_N, t_to_n => '0', + r_sel => R_T, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00001000", + ( -- r> + mem_op => MEM_NONE, + s_op => ST_INC, + r_op => ST_DEC, + alu_op => ALU_R, t_to_n => '1', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00001001", + ( -- call + mem_op => MEM_IMM32, + s_op => ST_NC, + r_op => ST_INC, + alu_op => ALU_T, t_to_n => '0', + r_sel => R_PC, + pc_sel => PC_MDR, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00001010", + ( -- ; + mem_op => MEM_NONE, + s_op => ST_NC, + r_op => ST_DEC, + alu_op => ALU_T, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_R, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00001011", + ( -- jmp + mem_op => MEM_IMM32, + s_op => ST_NC, + r_op => ST_NC, + alu_op => ALU_T, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_MDR, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00001100", + ( -- jz + mem_op => MEM_IMM32, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_N, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_MDR, + imask_set => '0', imask_clr => '0', + cond => C_ZERO + ) when "00001101", + ( -- jn + mem_op => MEM_IMM32, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_N, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_MDR, + imask_set => '0', imask_clr => '0', + cond => C_NEG + ) when "00001110", + ( -- jp + mem_op => MEM_IMM32, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_N, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_MDR, + imask_set => '0', imask_clr => '0', + cond => C_POS + ) when "00001111", + ( -- + + mem_op => MEM_NONE, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_ADD, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00010000", + ( -- - + mem_op => MEM_NONE, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_SUB, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00010001", + ( -- & + mem_op => MEM_NONE, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_AND, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00010010", + ( -- | + mem_op => MEM_NONE, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_OR, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00010011", + ( -- ^ + mem_op => MEM_NONE, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_XOR, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00010100", + ( -- ~ + mem_op => MEM_NONE, + s_op => ST_NC, + r_op => ST_NC, + alu_op => ALU_NOT, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00010101", + ( -- DUP + mem_op => MEM_NONE, + s_op => ST_INC, + r_op => ST_NC, + alu_op => ALU_T, t_to_n => '1', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00010110", + ( -- DROP + mem_op => MEM_NONE, + s_op => ST_DEC, + r_op => ST_NC, + alu_op => ALU_N, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00010111", + ( -- SWAP + mem_op => MEM_NONE, + s_op => ST_NC, + r_op => ST_NC, + alu_op => ALU_N, t_to_n => '1', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when "00011000", + ( -- imask_set + mem_op => MEM_NONE, + s_op => ST_NC, + r_op => ST_NC, + alu_op => ALU_T, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '1', imask_clr => '0', + cond => C_NONE + ) when "00011001", + ( -- imask_clr + mem_op => MEM_NONE, + s_op => ST_NC, + r_op => ST_NC, + alu_op => ALU_T, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '1', + cond => C_NONE + ) when "00011010", + ( -- NOP + mem_op => MEM_NONE, + s_op => ST_NC, + r_op => ST_NC, + alu_op => ALU_T, t_to_n => '0', + r_sel => R_NC, + pc_sel => PC_NC, + imask_set => '0', imask_clr => '0', + cond => C_NONE + ) when others; + + + -- Address multiplexer + with adr_sel select adr_o <= + pc_reg when ADR_PC, + s_t_reg when ADR_T, + s_t_reg(31 downto 2) & "00" when ADR_T0, + s_t_reg(31 downto 2) & "01" when ADR_T1, + s_t_reg(31 downto 2) & "10" when ADR_T2, + s_t_reg(31 downto 2) & "11" when ADR_T3, + (others => '0') when others; + + + -- Memory data register + process (clk_i, mdr_ld_0, mdr_ld_1, mdr_ld_2, mdr_ld_3) + begin + if rising_edge(clk_i) then + if mdr_ld_0 = '1' then + -- Zero-extend for 8-bit loads + -- Zeroes will be overwritten for 32-bit loads + mdr_reg( 7 downto 0) <= dat_i; + mdr_reg(31 downto 8) <= (others => '0'); + elsif mdr_ld_1 = '1' then + mdr_reg(15 downto 8) <= dat_i; + elsif mdr_ld_2 = '1' then + mdr_reg(23 downto 16) <= dat_i; + elsif mdr_ld_3 = '1' then + mdr_reg(31 downto 24) <= dat_i; + end if; + end if; + end process; + + with dat_sel select dat_o <= + s_n_reg(31 downto 24) when DAT_N3, + s_n_reg(23 downto 16) when DAT_N2, + s_n_reg(15 downto 8) when DAT_N1, + s_n_reg( 7 downto 0) when others; + + + -- ALU + with alu_op select s_t_new <= + s_t_reg when ALU_T, + s_n_reg when ALU_N, + std_logic_vector(signed(s_t_reg) + signed(s_n_reg)) when ALU_ADD, + std_logic_vector(signed(s_t_reg) - signed(s_n_reg)) when ALU_SUB, + s_t_reg and s_n_reg when ALU_AND, + s_t_reg or s_n_reg when ALU_OR, + s_t_reg xor s_n_reg when ALU_XOR, + not s_t_reg when ALU_NOT, + r_tos_reg when ALU_R, + mdr_reg when ALU_MDR, + x"000000" & vec_i when ALU_INTVEC, + (others => '0') when others; + + + -- Next R + with r_sel select r_tos_new <= + s_t_reg when R_T, + pc_reg when R_PC, + r_tos_reg when others; + + + -- Stack memory + p_s_tos: process (clk_i, s_t_ld, s_t_new) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + -- Reset to zero as the "reset vector" + s_t_reg <= (others => '0'); + elsif s_t_ld = '1' then + s_t_reg <= s_t_new; + end if; + end if; + end process; + + n <= s_t_reg(s_t_reg'high); + z <= not or_reduce(s_t_reg); + p <= (not n) and (not z); + + p_s_ptr: process (rst_i, clk_i, s_ptr_ld, s_ptr_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + s_ptr_reg <= (others => '0'); + else + if s_ptr_ld = '1' then + s_ptr_reg <= s_ptr_next; + end if; + end if; + end if; + end process; + + with s_ptr_op select s_ptr_next <= + s_ptr_reg + 1 when ST_INC, + s_ptr_reg - 1 when ST_DEC, + s_ptr_reg when others; + + p_r_ptr: process (rst_i, clk_i, r_ptr_ld, r_ptr_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + r_ptr_reg <= (others => '0'); + else + if r_ptr_ld = '1' then + r_ptr_reg <= r_ptr_next; + end if; + end if; + end if; + end process; + + with r_ptr_op select r_ptr_next <= + r_ptr_reg + 1 when ST_INC, + r_ptr_reg - 1 when ST_DEC, + r_ptr_reg when others; + + addra <= '0' & std_logic_vector(s_ptr_next); + addrb <= '1' & std_logic_vector(r_ptr_next); + + e_stack_mem: ramb16_s36_s36 + port map ( + -- Port A, parameter stack + ssra => rst_i, + clka => clk_i, + ena => '1', + wea => s_write, + addra => addra, + + doa => s_n_reg, + dopa => open, + dia => s_t_reg, + dipa => "0000", + + -- Port B, return stack + ssrb => rst_i, + clkb => clk_i, + enb => '1', + web => r_write, + addrb => addrb, + + dob => r_tos_reg, + dopb => open, + dib => r_tos_new, + dipb => "0000" + ); + + +end behavioral; diff --git a/projects/cpu_0/int_ctrl.vhd b/projects/cpu_0/int_ctrl.vhd new file mode 100644 index 0000000..72b52de --- /dev/null +++ b/projects/cpu_0/int_ctrl.vhd @@ -0,0 +1,33 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + + +entity int_ctrl is + port ( + int_o: out std_logic; + vec_o: out std_logic_vector(3 downto 0); + + int_i: in std_logic_vector(15 downto 0) + ); +end int_ctrl; + + +architecture behavioral of int_ctrl is +begin + + int_o <= or_reduce(int_i); + + process (int_i) + begin + vec_o <= (others => '0'); + + for i in 15 downto 0 loop + if int_i(i) = '1' then + vec_o <= std_logic_vector(to_unsigned(i, vec_o'length)); + end if; + end loop; + end process; + +end behavioral; diff --git a/projects/cpu_0/nexys2.vhd b/projects/cpu_0/nexys2.vhd new file mode 100644 index 0000000..c2f156d --- /dev/null +++ b/projects/cpu_0/nexys2.vhd @@ -0,0 +1,527 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; + +library utility; +library nexys2_lib; +library ps2; +library rs232; +library vga; + + +entity nexys2 is + port ( + -- Clock input + clk_50: in std_logic; + + -- EPP interface + DB: inout std_logic_vector(7 downto 0); + EppWRITE: in std_logic; + EppASTB: in std_logic; + EppDSTB: in std_logic; + EppWAIT: out std_logic; + + -- Memory interface + MemOE: out std_logic; + MemWR: out std_logic; + RamAdv: out std_logic; + RamCS: out std_logic; + RamClk: out std_logic; + RamCRE: out std_logic; + RamLB: out std_logic; + RamUB: out std_logic; + RamWait: in std_logic; + FlashRp: out std_logic; + FlashCS: out std_logic; + FlashStSts: in std_logic; + MemAdr: out std_logic_vector(23 downto 1); + MemDB: inout std_logic_vector(15 downto 0); + + -- Switches and LEDs + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + Led: out std_logic_vector(7 downto 0); + sw: in std_logic_vector(7 downto 0); + btn: in std_logic_vector(3 downto 0); + + -- VGA video output + vgaRed: out std_logic_vector(3 downto 1); + vgaGreen: out std_logic_vector(3 downto 1); + vgaBlue: out std_logic_vector(3 downto 2); + Hsync: out std_logic; + Vsync: out std_logic; + + -- PS2 (keyboard) + PS2C: inout std_logic; + PS2D: inout std_logic; + + -- RS232 (mouse) + RsRx: in std_logic; + RsTx: out std_logic + ); +end nexys2; + + +architecture behavioral of nexys2 is + + -- Device Wishbone SYSCON + signal g_rst: std_logic; -- Global power-on-reset + signal h_rst: std_logic; -- Host controller reset + signal d_rst: std_logic; -- Device reset (P.O.R. or host) + signal d_clk: std_logic; -- Device clock, in case DCM used instead of clk_50 later + + -- Wishbone busses + signal cyc: std_logic; + signal stb: std_logic; + signal we: std_logic; + signal ack: std_logic; + signal adr: std_logic_vector(31 downto 0); + signal dat_mosi: std_logic_vector(7 downto 0); + signal dat_miso: std_logic_vector(7 downto 0); + + signal mem_cyc: std_logic; + signal mem_stb: std_logic; + signal mem_we: std_logic; + signal mem_ack: std_logic; + signal mem_adr: std_logic_vector(31 downto 0); + signal mem_mosi: std_logic_vector(7 downto 0); + signal mem_miso: std_logic_vector(7 downto 0); + + signal tile_cyc: std_logic; + signal tile_stb: std_logic; + signal tile_we: std_logic; + signal tile_ack: std_logic; + signal tile_adr: std_logic_vector(31 downto 0); + signal tile_mosi: std_logic_vector(7 downto 0); + signal tile_miso: std_logic_vector(7 downto 0); + + signal host_cyc: std_logic; + signal host_stb: std_logic; + signal host_we: std_logic; + signal host_ack: std_logic; + signal host_adr: std_logic_vector(31 downto 0); + signal host_mosi: std_logic_vector(7 downto 0); + signal host_miso: std_logic_vector(7 downto 0); + + signal ps2_cyc: std_logic; + signal ps2_stb: std_logic; + signal ps2_we: std_logic; + signal ps2_ack: std_logic; + signal ps2_adr: std_logic_vector(31 downto 0); + signal ps2_mosi: std_logic_vector(7 downto 0); + signal ps2_miso: std_logic_vector(7 downto 0); + + signal uart_cyc: std_logic; + signal uart_stb: std_logic; + signal uart_we: std_logic; + signal uart_ack: std_logic; + signal uart_adr: std_logic_vector(31 downto 0); + signal uart_mosi: std_logic_vector(7 downto 0); + signal uart_miso: std_logic_vector(7 downto 0); + + -- Interrupt signals + signal host_flags: std_logic_vector(7 downto 0); + + signal ps2_rx_ready: std_logic; + signal ps2_rx_full: std_logic; + signal ps2_tx_ready: std_logic; + signal ps2_tx_empty: std_logic; + signal ps2_err_nack: std_logic; + signal ps2_err_txto: std_logic; + signal ps2_err_rxto: std_logic; + signal ps2_err_missed: std_logic; + signal ps2_err_parity: std_logic; + signal ps2_err_framing: std_logic; + + signal uart_rx_ready: std_logic; + signal uart_rx_full: std_logic; + signal uart_tx_ready: std_logic; + signal uart_tx_empty: std_logic; + signal uart_err_break: std_logic; + signal uart_err_framing: std_logic; + signal uart_err_parity: std_logic; + signal uart_err_overflow: std_logic; + + -- Memory physical interface, routed through host controller + signal d_MemOE: std_logic; + signal d_MemWR: std_logic; + signal d_RamAdv: std_logic; + signal d_RamCS: std_logic; + signal d_RamClk: std_logic; + signal d_RamCRE: std_logic; + signal d_RamUB: std_logic; + signal d_RamLB: std_logic; + signal d_RamWait: std_logic; + signal d_FlashRp: std_logic; + signal d_FlashCS: std_logic; + signal d_FlashStSts: std_logic; + signal d_MemAdr: std_logic_vector(23 downto 1); + signal d_MemDB_i: std_logic_vector(15 downto 0); + signal d_MemDB_o: std_logic_vector(15 downto 0); + + -- Interrupt signals + signal int_cpu: std_logic; + signal int_vec: std_logic_vector(7 downto 0); + signal ints: std_logic_vector(15 downto 0); + + signal debug_i: std_logic_vector(63 downto 0); + signal debug_o: std_logic_vector(63 downto 0); + +begin + + Led <= mem_miso; + e_cpu: entity work.cpu + port map ( + rst_i => d_rst, + clk_i => d_clk, + + cyc_o => cyc, + stb_o => stb, + we_o => we, + ack_i => ack, + adr_o => adr, + dat_o => dat_mosi, + dat_i => dat_miso, + + int_i => int_cpu, + vec_i => int_vec, + + debug_i => debug_o, + debug_o => debug_i + ); + + int_vec(7 downto 4) <= (others => '0'); + ints(0) <= '0'; + e_int: entity work.int_ctrl + port map ( + int_o => int_cpu, + vec_o => int_vec(3 downto 0), + int_i => ints + ); + + d_clk <= clk_50; + + e_por: entity utility.power_on_reset + port map ( + rst_i => '0', + clk_i => d_clk, + rst_o => g_rst + ); + + d_rst <= g_rst or h_rst; + + -- Maybe it would be more understandable if the mapper just produced select + -- signals from mask/match/address inputs? Then each device can and the + -- global cyc signal with its own select signal, take the rest of the bus + -- signals as they are, produce their own miso+ack, then have a separate mux + -- for misos+acks? + e_mapper: entity utility.wb_mapper + generic map ( + A_WIDTH => 32, + D_WIDTH => 8 + ) + port map ( + cyc_i => cyc, + stb_i => stb, + we_i => we, + ack_o => ack, + adr_i => adr, + dat_i => dat_mosi, + dat_o => dat_miso, + + -- Flash: 0x00000000-0x00ffffff + -- Ram: 0x01000000-0x01ffffff + mask_0 => "00000010000000000000000000000000", + match_0 => "00000000000000000000000000000000", + cyc_o_0 => mem_cyc, + stb_o_0 => mem_stb, + we_o_0 => mem_we, + ack_i_0 => mem_ack, + adr_o_0 => mem_adr, + dat_o_0 => mem_mosi, + dat_i_0 => mem_miso, + + -- Vbuf: 0x02000000-0x02001fff + -- Tiles: 0x02002000-0x02003fff + mask_1 => "00000010000000000100000000000000", + match_1 => "00000010000000000000000000000000", + cyc_o_1 => tile_cyc, + stb_o_1 => tile_stb, + we_o_1 => tile_we, + ack_i_1 => tile_ack, + adr_o_1 => tile_adr, + dat_o_1 => tile_mosi, + dat_i_1 => tile_miso, + + -- Host: 0x02004000-0x02004007 + mask_2 => "00000010000000000100000000011000", + match_2 => "00000010000000000100000000000000", + cyc_o_2 => host_cyc, + stb_o_2 => host_stb, + we_o_2 => host_we, + ack_i_2 => host_ack, + adr_o_2 => host_adr, + dat_o_2 => host_mosi, + dat_i_2 => host_miso, + + -- PS2: 0x02004008-0x0200400f + mask_3 => "00000010000000000100000000011000", + match_3 => "00000010000000000100000000001000", + cyc_o_3 => ps2_cyc, + stb_o_3 => ps2_stb, + we_o_3 => ps2_we, + ack_i_3 => ps2_ack, + adr_o_3 => ps2_adr, + dat_o_3 => ps2_mosi, + dat_i_3 => ps2_miso, + + -- UART: 0x02004010-0x02004017 + mask_4 => "00000010000000000100000000011000", + match_4 => "00000010000000000100000000010000", + cyc_o_4 => uart_cyc, + stb_o_4 => uart_stb, + we_o_4 => uart_we, + ack_i_4 => uart_ack, + adr_o_4 => uart_adr, + dat_o_4 => uart_mosi, + dat_i_4 => uart_miso, + + mask_5 => "00000000000000000000000000000000", + match_5 => "00000000000000000000000000000001", + cyc_o_5 => open, + stb_o_5 => open, + we_o_5 => open, + ack_i_5 => '1', + adr_o_5 => open, + dat_o_5 => open, + dat_i_5 => "00000000", + + mask_6 => "00000000000000000000000000000000", + match_6 => "00000000000000000000000000000001", + cyc_o_6 => open, + stb_o_6 => open, + we_o_6 => open, + ack_i_6 => '1', + adr_o_6 => open, + dat_o_6 => open, + dat_i_6 => "00000000", + + mask_7 => "00000000000000000000000000000000", + match_7 => "00000000000000000000000000000001", + cyc_o_7 => open, + stb_o_7 => open, + we_o_7 => open, + ack_i_7 => '1', + adr_o_7 => open, + dat_o_7 => open, + dat_i_7 => "00000000" + ); + + + e_mem: entity nexys2_lib.mem_wb8_0 + port map ( + rst_i => d_rst, + clk_i => d_clk, + + -- Internal access + cyc_i => mem_cyc, + stb_i => mem_stb, + we_i => mem_we, + ack_o => mem_ack, + adr_i => mem_adr(24 downto 0), + dat_i => mem_mosi, + dat_o => mem_miso, + + -- Configuration + wait_cycles => "01100", + + -- Memory interface + MemOE => d_MemOE, + MemWR => d_MemWR, + RamAdv => d_RamAdv, + RamCS => d_RamCS, + RamClk => d_RamClk, + RamCRE => d_RamCRE, + RamUB => d_RamUB, + RamLB => d_RamLB, + RamWait => d_RamWait, + FlashRp => d_FlashRp, + FlashCS => d_FlashCS, + FlashStSts => d_FlashStSts, + MemAdr => d_MemAdr, + MemDB_i => d_MemDB_i, + MemDB_o => d_MemDB_o + ); + + + e_vga: entity vga.vga_tiler + port map ( + rst_i => d_rst, + clk_i => d_clk, + + -- Internal access to screen buffer and tile set + cyc_i => tile_cyc, + stb_i => tile_stb, + we_i => tile_we, + ack_o => tile_ack, + adr_i => tile_adr(13 downto 0), + dat_i => tile_mosi, + dat_o => tile_miso, + + -- External pins + vgaRed => vgaRed, + vgaGreen => vgaGreen, + vgaBlue => vgaBlue, + Hsync => Hsync, + Vsync => Vsync + ); + + + ints(1) <= or_reduce(host_flags); + e_host: entity nexys2_lib.host_ctrl + port map ( + clk_i => d_clk, + rst_i => g_rst, + + -- Signals to the internal device + d_rst_o => h_rst, + d_flags_o => host_flags, + debug_i => debug_i, + debug_o => debug_o, + + -- Internal access to control registers + d_cyc_i => host_cyc, + d_stb_i => host_stb, + d_we_i => host_we, + d_ack_o => host_ack, + d_adr_i => host_adr(2 downto 0), + d_dat_i => host_mosi, + d_dat_o => host_miso, + + -- Internal memory interface, can be switched off to allow the host to control memory + d_MemOE => d_MemOE, + d_MemWR => d_MemWR, + d_RamAdv => d_RamAdv, + d_RamCS => d_RamCS, + d_RamClk => d_RamClk, + d_RamCRE => d_RamCRE, + d_RamLB => d_RamLB, + d_RamUB => d_RamUB, + d_RamWait => d_RamWait, + d_FlashRp => d_FlashRp, + d_FlashCS => d_FlashCS, + d_FlashStSts => d_FlashStSts, + d_MemAdr => d_MemAdr, + d_MemDB_o => d_MemDB_o, + d_MemDB_i => d_MemDB_i, + + -- External pins + EppAstb => EppASTB, + EppDstb => EppDSTB, + EppWr => EppWRITE, + EppDB => DB, + EppWait => EppWAIT, + + MemOE => MemOE, + MemWR => MemWR, + RamAdv => RamAdv, + RamCS => RamCS, + RamClk => RamClk, + RamCRE => RamCRE, + RamLB => RamLB, + RamUB => RamUB, + RamWait => RamWait, + FlashRp => FlashRp, + FlashCS => FlashCS, + FlashStSts => FlashStSts, + MemAdr => MemAdr, + MemDB => MemDB, + + seg => seg, + dp => dp, + an => an, + Led => open, + sw => sw + ); + + + ints(2) <= ps2_rx_ready or + ps2_rx_full or + ps2_tx_ready or + ps2_tx_empty or + ps2_err_nack or + ps2_err_txto or + ps2_err_rxto or + ps2_err_missed or + ps2_err_parity or + ps2_err_framing; + e_ps2: entity ps2.ps2_host_wb + port map ( + rst_i => d_rst, + clk_i => d_clk, + + -- Internal access + cyc_i => ps2_cyc, + stb_i => ps2_stb, + we_i => ps2_we, + ack_o => ps2_ack, + adr_i => ps2_adr(2 downto 0), + dat_i => ps2_mosi, + dat_o => ps2_miso, + + -- Interrupt signals + rx_ready => ps2_rx_ready, + rx_full => ps2_rx_full, + tx_ready => ps2_tx_ready, + tx_empty => ps2_tx_empty, + err_nack => ps2_err_nack, + err_txto => ps2_err_txto, + err_rxto => ps2_err_rxto, + err_missed => ps2_err_missed, + err_parity => ps2_err_parity, + err_framing => ps2_err_framing, + + -- External pins + ps2_clk => PS2C, + ps2_data => PS2D + ); + + + ints(3) <= uart_rx_ready or + uart_rx_full or + uart_tx_ready or + uart_tx_empty or + uart_err_break or + uart_err_framing or + uart_err_overflow; + e_rs232: entity rs232.rs232_uart + port map ( + rst_i => d_rst, + clk_i => d_clk, + + -- Internal access + cyc_i => uart_cyc, + stb_i => uart_stb, + we_i => uart_we, + ack_o => uart_ack, + adr_i => uart_adr(2 downto 0), + dat_i => uart_mosi, + dat_o => uart_miso, + + -- Interrupt signals + rx_ready => uart_rx_ready, + rx_full => uart_rx_full, + tx_ready => uart_tx_ready, + tx_empty => uart_tx_empty, + err_break => uart_err_break, + err_framing => uart_err_framing, + err_parity => uart_err_parity, + err_overflow => uart_err_overflow, + + -- External pins + tx => RsTx, + rx => RsRx + ); + +end behavioral; diff --git a/projects/cpu_0/program.sh b/projects/cpu_0/program.sh new file mode 100755 index 0000000..ac51e3b --- /dev/null +++ b/projects/cpu_0/program.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog + diff --git a/projects/cpu_0/tests/test_cpu.vhd b/projects/cpu_0/tests/test_cpu.vhd new file mode 100644 index 0000000..183cc3f --- /dev/null +++ b/projects/cpu_0/tests/test_cpu.vhd @@ -0,0 +1,111 @@ +library ieee; +use ieee.std_logic_1164.all; + +library simulated; +library work; + + +entity test_cpu is +end test_cpu; + + +architecture behavior of test_cpu is + + constant clk_i_period: time := 10 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal cyc_o: std_logic; + signal stb_o: std_logic; + signal we_o: std_logic; + signal ack_i: std_logic; + signal adr_o: std_logic_vector(31 downto 0); + signal dat_o: std_logic_vector(7 downto 0); + signal dat_i: std_logic_vector(7 downto 0); + + signal int_i: std_logic; + signal vec_i: std_logic_vector(7 downto 0); + +begin + + p_test: process + begin + -- Initial values + int_i <= '0'; + vec_i <= x"00"; + + -- Reset + rst_i <= '1'; + wait for clk_i_period * 2; + rst_i <= '0'; + + -- Fire interrupt + wait for 200 ns; + vec_i <= x"c3"; + int_i <= '1'; + + -- Done + wait; + end process; + + + e_mem: entity simulated.wb_memory + generic map ( + -- Test param stack, some math, and store + -- #0x24 #0x65 #0x03 #0x05 - + #0xdeadbeef ! + -- INIT => x"0124016501030105111002deadbeef0600000000000000000000000000000000" + + -- Test pushes and pops of both stacks + -- #0x11 #0x22 #0x33 #0x44 #0x55 #0x66 >R >R >R >R >R >R R> R> R> R> R> R> + -- INIT => x"0111012201330144015501660808080808080909090909090000000000000000" + + -- Test call, jmp and return + -- call 0x10 jmp 0x5 + -- INIT => x"0a100000000c05000000000000000000015a0b00000000000000000000000000" + + -- Test conditional branch and interrupts + -- 0x00: jz 0x10 #0xa5 ; 0x10: jmp 0x10 + INIT => x"0d1000000001a50b00000000000000000c100000000000000000000000000000" + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + cyc_i => cyc_o, + stb_i => stb_o, + we_i => we_o, + ack_o => ack_i, + adr_i => adr_o(4 downto 0), + dat_i => dat_o, + dat_o => dat_i + ); + + + e_uut: entity work.cpu + port map ( + rst_i => rst_i, + clk_i => clk_i, + + cyc_o => cyc_o, + stb_o => stb_o, + we_o => we_o, + ack_i => ack_i, + adr_o => adr_o, + dat_o => dat_o, + dat_i => dat_i, + + int_i => int_i, + vec_i => vec_i + ); + + + p_clk: process + begin + clk_i <= '0'; + wait for clk_i_period/2; + clk_i <= '1'; + wait for clk_i_period/2; + end process; + +end; diff --git a/projects/cpu_0/tests/test_intctrl.vhd b/projects/cpu_0/tests/test_intctrl.vhd new file mode 100644 index 0000000..623f6b9 --- /dev/null +++ b/projects/cpu_0/tests/test_intctrl.vhd @@ -0,0 +1,56 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity test_intctrl is +end test_intctrl; + + +architecture behavior of test_intctrl is + + signal int_i: std_logic_vector(15 downto 0); + signal int_o : std_logic; + signal vec_o : std_logic_vector(3 downto 0); + +begin + + p_test: process + begin + -- Initial values + int_i <= "0000000000000000"; + wait for 1 ns; + assert int_o = '0'; + + wait for 10 ns; + + int_i <= "0000000000001000"; + wait for 1 ns; + assert int_o = '1'; + assert vec_o = x"3"; + + wait for 10 ns; + + int_i <= "0001000000001000"; + wait for 1 ns; + assert int_o = '1'; + assert vec_o = x"3"; + + wait for 10 ns; + + int_i <= "0001000000000000"; + wait for 1 ns; + assert int_o = '1'; + assert vec_o = x"c"; + + -- Done + wait; + end process; + + e_uut: entity work.int_ctrl + port map ( + int_o => int_o, + vec_o => vec_o, + int_i => int_i + ); + +end; diff --git a/projects/cpu_0/tests/test_nexys2.vhd b/projects/cpu_0/tests/test_nexys2.vhd new file mode 100644 index 0000000..d8010b2 --- /dev/null +++ b/projects/cpu_0/tests/test_nexys2.vhd @@ -0,0 +1,162 @@ +library ieee; +use ieee.std_logic_1164.all; + +library simulated; +library work; + + +entity test_nexys2 is +end test_nexys2; + + +architecture behavior of test_nexys2 is + + constant clk_50_period: time := 20 ns; + + signal clk_50: std_logic; + + signal EppWRITE: std_logic; + signal EppASTB: std_logic; + signal EppDSTB: std_logic; + signal DB: std_logic_vector(7 downto 0); + signal EppWAIT: std_logic; + + signal MemOE: std_logic; + signal MemWR: std_logic; + signal RamAdv: std_logic; + signal RamCS: std_logic; + signal RamClk: std_logic; + signal RamCRE: std_logic; + signal RamLB: std_logic; + signal RamUB: std_logic; + signal RamWait: std_logic; + signal FlashRp: std_logic; + signal FlashCS: std_logic; + signal FlashStSts: std_logic; + signal MemAdr: std_logic_vector(23 downto 1); + signal MemDB: std_logic_vector(15 downto 0); + + signal vgaRed: std_logic_vector(3 downto 1); + signal vgaGreen: std_logic_vector(3 downto 1); + signal vgaBlue: std_logic_vector(3 downto 2); + signal Hsync: std_logic; + signal Vsync: std_logic; + + signal sw: std_logic_vector(7 downto 0); + signal btn: std_logic_vector(3 downto 0); + signal seg: std_logic_vector(6 downto 0); + signal dp: std_logic; + signal an: std_logic_vector(3 downto 0); + signal Led: std_logic_vector(7 downto 0); + + signal PS2C: std_logic; + signal PS2D: std_logic; + + signal RsRx: std_logic; + signal RsTx: std_logic; + + signal flash_ce: std_logic_vector(2 downto 0); + signal flash_adr: std_logic_vector(23 downto 0); + +begin + + + p_test: process + begin + -- Initial values + EppWRITE <= '1'; + EppASTB <= '1'; + EppDSTB <= '1'; + DB <= (others => 'Z'); + sw <= (others => '0'); + btn <= (others => '0'); + PS2C <= 'H'; + PS2D <= 'H'; + RsRx <= '0'; + + -- Done + wait; + end process; + + + flash_ce <= "00" & FlashCS; + flash_adr <= MemAdr & '0'; + e_flash: entity simulated.js28f128j3d75 + port map ( + a => flash_adr, + d => MemDB, + ce => flash_ce, + rp_n => FlashRp, + oe_n => MemOE, + we_n => MemWR, + sts => FlashStSts, + byte_n => '1', + vpen => '1' + ); + + + e_ram: entity simulated.mt45w8mw16bgx + port map ( + a => MemAdr, + dq => MemDB, + clk => RamClk, + adv_n => RamAdv, + cre => RamCRE, + ce_n => RamCS, + oe_n => MemOE, + we_n => MemWR, + lb_n => RamLB, + ub_n => RamUB, + rwait => RamWait + ); + + + e_uut: entity work.nexys2 + port map ( + clk_50 => clk_50, + DB => DB, + EppWRITE => EppWRITE, + EppASTB => EppASTB, + EppDSTB => EppDSTB, + EppWAIT => EppWAIT, + MemOE => MemOE, + MemWR => MemWR, + RamAdv => RamAdv, + RamCS => RamCS, + RamClk => RamClk, + RamCRE => RamCRE, + RamLB => RamLB, + RamUB => RamUB, + RamWait => RamWait, + FlashRp => FlashRp, + FlashCS => FlashCS, + FlashStSts => FlashStSts, + MemAdr => MemAdr, + MemDB => MemDB, + seg => seg, + dp => dp, + an => an, + Led => Led, + sw => sw, + btn => btn, + vgaRed => vgaRed, + vgaGreen => vgaGreen, + vgaBlue => vgaBlue, + Hsync => Hsync, + Vsync => Vsync, + PS2C => PS2C, + PS2D => PS2D, + RsRx => RsRx, + RsTx => RsTx + ); + + + p_clk_50: process + begin + clk_50 <= '0'; + wait for clk_50_period/2; + clk_50 <= '1'; + wait for clk_50_period/2; + end process; + +end; diff --git a/projects/nexys2_host_controller/device/program.sh b/projects/nexys2_host_controller/device/program.sh new file mode 100755 index 0000000..14d6689 --- /dev/null +++ b/projects/nexys2_host_controller/device/program.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +djtgcfg -d Nexys2 -i 0 -f nexys2.bit prog +#djtgcfg -d Nexys2 -i 0 -f OnBoardMemCfg.bit prog + diff --git a/projects/nexys2_host_controller/host/build.sh b/projects/nexys2_host_controller/host/build.sh new file mode 100755 index 0000000..dcc8e1e --- /dev/null +++ b/projects/nexys2_host_controller/host/build.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +INC=/usr/include/digilent/adept +LIBDIR=/usr/lib64/digilent/adept + +CFLAGS="-std=c2x -Wall -Wextra -Wpedantic -D_POSIX_C_SOURCE" + +gcc -o epp_read epp_read.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr +gcc -o epp_write epp_write.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr +gcc -o digdude digdude.c fileformat.c memctrl.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldepp -ldmgr + diff --git a/projects/nexys2_host_controller/host/digdude.c b/projects/nexys2_host_controller/host/digdude.c new file mode 100644 index 0000000..135d73b --- /dev/null +++ b/projects/nexys2_host_controller/host/digdude.c @@ -0,0 +1,498 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 :r|w|v:[:format]\n" + " Carry out memory operation,\n" + " multiple -U options can be specified\n" + " -D, --noerase Disable auto-erase for flash memory\n" + " -e, --erase Perform a full flash erase\n" + " -E, --exitspec Specify device state upon completion\n" + " -n, --test-memory Do not write to device during -U\n" + " -V, --noverify-memory Do not automatically verify during -U\n" + "\nOutput options:\n" + " -v, --verbose Verbose output; -v -v for more\n" + " -q, --quell Quell progress output; -q -q for less\n" + " -?, --help Display this message\n" + "\n" + ); +} + + +struct update +{ + struct iter iter; + enum mem mem; + enum mode mode; + enum format format; +}; + + +bool parse_update(char *optarg, struct update *update_out) +{ + assert(optarg != NULL); + assert(update_out != NULL); + + char *_optarg; + char *token; + char *filename; + + EC_NULL(token = strtok_r(optarg, ":", &_optarg)); + if (!strcmp(token, "ram")) + { + update_out->mem = RAM; + } + else if (!strcmp(token, "flash")) + { + update_out->mem = FLASH; + } + else if (!strcmp(token, "cfi")) + { + update_out->mem = CFI; + } + else + { + EC_FAIL; + } + + EC_NULL(token = strtok_r(NULL, ":", &_optarg)); + if (strlen(token) > 1) EC_FAIL; + switch (token[0]) + { + case 'r': update_out->mode = M_READ; break; + case 'w': update_out->mode = M_WRITE; break; + case 'v': update_out->mode = M_VERIFY; break; + default: EC_FAIL; + } + + EC_NULL(token = strtok_r(NULL, ":", &_optarg)); + filename = token; + + update_out->format = F_RAWBIN; + token = strtok_r(NULL, ":", &_optarg); + if (token != NULL) + { + switch (token[0]) + { + case 'i': update_out->format = F_INTELHEX; break; + case 's': update_out->format = F_MOTOSREC; break; + case 'b': update_out->format = F_RAWBIN; break; + default: EC_FAIL; + } + } + + if (update_out->mode == M_READ) + { + // Read from memory, write to file + EC_FALSE(iter_write(&update_out->iter, filename, update_out->format)); + } + else + { + // Write/verify memory, read from file + EC_FALSE(iter_read(&update_out->iter, filename, update_out->format)); + } + return true; + +EC_CLEANUP_BEGIN + return false; +EC_CLEANUP_END +} + + +#define MAX_UPDATES 8 + + +int main(int argc, char **argv) +{ + // Parsed options + bool erase_auto = true; + bool erase_explicit = false; + char *part = "Nexys2"; + int port = 0; + int quell = 0; + int verbose = 0; + bool verify = true; + enum exitspec exitspec = X_RESET; + struct update updates[MAX_UPDATES]; + size_t num_updates = 0; + + // Board connection handle + HIF hif; + + // Option parsing + int opt; + int option_idx = 0; + struct option longopts[] = { + {"help", no_argument, NULL, '?'}, + {"noerase", no_argument, NULL, 'D'}, + {"erase", no_argument, NULL, 'e'}, + {"exitspec", required_argument, NULL, 'E'}, + {"test-memory", no_argument, NULL, 'n'}, + {"part", required_argument, NULL, 'p'}, + {"port", required_argument, NULL, 'P'}, + {"quell", no_argument, NULL, 'q'}, + {"memory", required_argument, NULL, 'U'}, + {"verbose", no_argument, NULL, 'v'}, + {"noverify-memory", no_argument, NULL, 'V'}, + }; + + while ((opt = getopt_long(argc, argv, "?DeE:l:p:P:qU:vV", longopts, &option_idx)) != -1) + { + switch (opt) + { + case '?': usage(); exit(0); break; + case 'D': erase_auto = false; break; + case 'e': erase_explicit = true; erase_auto = false; break; + + case 'E': + if (!strcmp(optarg, "reset")) + { + exitspec = X_RESET; + } + else if (!strcmp(optarg, "noreset")) + { + exitspec = X_NORESET; + } + else + { + EC_FAIL; + } + break; + + case 'p': part = optarg; break; + case 'P': port = strtol(optarg, NULL, 0); break; + case 'q': quell++; break; + + case 'U': + if (num_updates >= MAX_UPDATES) EC_FAIL; + EC_FALSE(parse_update(optarg, &updates[num_updates])); + num_updates++; + break; + + case 'v': verbose++; break; + case 'V': verify = false; break; + + default: + fprintf(stderr, "Invalid option: -%c\n\n", opt); + usage(); + exit(1); + break; + } + } + + + // Open connection to board + EC_FALSE(epp_open(&hif, part, port)); + + // Optional explicit full-chip erase + if (erase_explicit) EC_FALSE(update_flash_erase(hif)); + + // Possibly multiple updates + for (size_t i = 0; i < num_updates; i++) + { + switch (updates[i].mem) + { + case RAM: + switch (updates[i].mode) + { + case M_READ: EC_FALSE(update_ram_read(hif, &updates[i].iter, 0x000000, 0x800000)); break; + case M_WRITE: EC_FALSE(update_ram_write(hif, &updates[i].iter, verify)); break; + case M_VERIFY: EC_FALSE(update_ram_verify(hif, &updates[i].iter)); break; + default: assert(false); break; + } + break; + + case FLASH: + switch (updates[i].mode) + { + case M_READ: EC_FALSE(update_flash_read(hif, &updates[i].iter, 0x000000, 0x800000)); break; + case M_WRITE: EC_FALSE(update_flash_write(hif, &updates[i].iter, erase_auto, verify)); break; + case M_VERIFY: EC_FALSE(update_flash_verify(hif, &updates[i].iter)); break; + default: assert(false); break; + } + break; + + case CFI: + switch (updates[i].mode) + { + case M_READ: + //EC_FALSE(flash_cfi(hif, 0, buffer, sizeof(buffer))); + //EC_FALSE(write_file(updates[i].filename, updates[i].format, buffer, sizeof(buffer))); + break; + + case M_WRITE: EC_FAIL; break; + case M_VERIFY: EC_FAIL; break; + default: assert(false); break; + } + break; + + default: assert(false); break; + } + } + + // Return to desired exit condition + switch (exitspec) + { + case X_RESET: EC_FALSE(set_exit(hif, true)); break; + case X_NORESET: EC_FALSE(set_exit(hif, false)); break; + default: assert(false); break; + } + + // Close connection to board and done + epp_close(hif); + return 0; + +EC_CLEANUP_BEGIN + epp_close(hif); + ec_print(); + return 1; +EC_CLEANUP_END +} + diff --git a/projects/nexys2_host_controller/host/ec.c b/projects/nexys2_host_controller/host/ec.c new file mode 100644 index 0000000..d61d522 --- /dev/null +++ b/projects/nexys2_host_controller/host/ec.c @@ -0,0 +1,71 @@ +#include "ec.h" + +#define EC_MAX 32 + + +// ----------------------------------------------------------------------------- + + +struct ec_node +{ + int ec_errno; + EC_ERRTYPE type; + const char *func; + const char *file; + int line; + const char *str_line; +}; + + +static struct ec_node ec_stack[EC_MAX]; +static size_t ec_top = 0; + + +// ----------------------------------------------------------------------------- + + +const bool ec_in_cleanup = false; + + +void ec_push(const char *func, const char *file, int line, const char *str_line, + int errno_arg, EC_ERRTYPE type) +{ + assert(ec_top < EC_MAX); + + ec_stack[ec_top].ec_errno = errno_arg; + ec_stack[ec_top].type = type; + ec_stack[ec_top].func = func; + ec_stack[ec_top].file = file; + ec_stack[ec_top].line = line; + ec_stack[ec_top].str_line = str_line; + + ec_top++; +} + + +void ec_print(void) +{ + fprintf(stderr, "ERROR STACK:\n"); + for (int i = ec_top - 1; i >= 0; i--) + { + fprintf(stderr, "%3i: %s:%i (%s) %s\n", + i, + ec_stack[i].file, + ec_stack[i].line, + ec_stack[i].func, + ec_stack[i].str_line); + } +} + + +void ec_reinit(void) +{ + ec_top = 0; +} + + +void ec_warn(void) +{ + fprintf(stderr, "WARNING: Control flowed into EC_CLEANUP_BEGIN\n"); +} + diff --git a/projects/nexys2_host_controller/host/ec.h b/projects/nexys2_host_controller/host/ec.h new file mode 100644 index 0000000..f99556f --- /dev/null +++ b/projects/nexys2_host_controller/host/ec.h @@ -0,0 +1,95 @@ +/* + +Shamelessly stolen from Advanced UNIX Programming 2nd Ed. by Marc J. Rochkind + +*/ + +#pragma once + +#include +#include +#include +#include +#include + + +extern const bool ec_in_cleanup; + + +typedef enum { + EC_ERRNO, + EC_EAI, + EC_GETDATE, + EC_NONE, +} EC_ERRTYPE; + + +#define EC_CLEANUP_BEGIN \ + ec_warn(); \ + ec_cleanup_begin: \ + { \ + bool ec_in_cleanup = true; \ + (void)ec_in_cleanup; // Suppress "unused" warning + + +#define EC_CLEANUP_END \ + } + + +// For functions that set ERRNO and return a specific error indicator value +#define EC_CMP(var, errrtn) \ + { \ + assert(!ec_in_cleanup); \ + if ((intptr_t)(var) == (intptr_t)(errrtn)) { \ + ec_push(__func__, __FILE__, __LINE__, #var, errno, EC_ERRNO); \ + goto ec_cleanup_begin; \ + } \ + } + + +// For functions that return an ERRNO value +#define EC_RV(var) \ + { \ + int errrtn; \ + assert(!ec_in_cleanup); \ + if ((errrtn = (var)) != 0) { \ + ec_push(__func__, __FILE__, __LINE__, #var, errrtn, EC_ERRNO); \ + goto ec_cleanup_begin; \ + } \ + } + + +#define EC_AI(var) \ + { \ + int errrtn; \ + assert(!ec_in_cleanup); \ + if ((errrtn = (var)) != 0) { \ + ec_push(__func__, __FILE__, __LINE__, #var, errrtn, EC_EAI); \ + goto ec_cleanup_begin; \ + } \ + } + + +#define EC_NEG1(var) EC_CMP(var, -1) +#define EC_NULL(var) EC_CMP(var, NULL) +#define EC_FALSE(var) EC_CMP(var, false) +#define EC_EOF(var) EC_CMP(var, EOF) +#define EC_NZ(var) { if ((var) != 0) EC_FAIL } +#define EC_NE(var, errrtn) { if ((var) != (errrtn)) EC_FAIL } + +#define EC_FAIL EC_CMP(0, 0) +#define EC_CLEANUP goto ec_cleanup_begin; + +#define EC_FLUSH(str) \ + { \ + ec_print(); \ + ec_reinit(); \ + } + + +void ec_push(const char *func, const char *file, int line, const char *str_line, + int errno_arg, EC_ERRTYPE type); +void ec_print(void); +void ec_reinit(void); +void ec_warn(void); + diff --git a/projects/nexys2_host_controller/host/epp_read.c b/projects/nexys2_host_controller/host/epp_read.c new file mode 100644 index 0000000..2cff490 --- /dev/null +++ b/projects/nexys2_host_controller/host/epp_read.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include + +// Digilent SDK +#include "dpcdecl.h" +#include "depp.h" +#include "dmgr.h" + +#include "ec.h" + + +// ----------------------------------------------------------------------------- + + +int main(int argc, char **argv) +{ + long address; + uint8_t value; + HIF hif = hifInvalid; + char *device = "Nexys2"; + int32_t port_count; + uint32_t port_properties; + int32_t port = 0; + char version[cchVersionMax]; + + // ----------------------------------------------------------- + // Parse commnd line args + + if (argc != 2) + { + fprintf(stderr, "Wrong number of arguments\n"); + return 1; + } + + address = strtol(argv[1], NULL, 0); // TODO: error handling + + // ----------------------------------------------------------- + // Initilize EPP interface + + // Print DEPP version + EC_FALSE(DeppGetVersion(version)); + //printf("DEPP version: %s\n", version); + + // Open device + EC_FALSE(DmgrOpen(&hif, device)); + //printf("Opened device \"%s\"\n", device); + + // Check number of DEPP ports + EC_FALSE(DeppGetPortCount(hif, &port_count)); + //printf("Port count: %" PRIi32 "\n", port_count); + for (int32_t i = 0; i < port_count; i++) + { + EC_FALSE(DeppGetPortProperties(hif, i, &port_properties)); + //printf("Port %" PRIi32 " properties: 0x%08" PRIx32 "\n", i, port_properties); + } + if (port_count != 1) + { + fprintf(stderr, "TODO: number of DEPP ports: %" PRIi32 "\n", port_count); + EC_FAIL; + } + + // Enable DEPP port + EC_FALSE(DeppEnableEx(hif, port)) + //printf("Opened EPP port %" PRIi32 "\n", port); + + // Set timeout + // uint32_t timeout; + // EC_FALSE(DeppSetTimeout(hif, 0xffffffff, &timeout)); + // printf("Timeout set to %" PRIu32 "ns\n", timeout); + + // ----------------------------------------------------------- + // Read EPP register and report + + EC_FALSE(DeppGetReg(hif, address, &value, false)); + printf("Read: 0x%02lx == 0x%02x\n", address, value); + + // ----------------------------------------------------------- + // Close EPP session + + if (hif != hifInvalid) + { + DeppDisable(hif); + DmgrClose(hif); + //printf("Closed session\n"); + } + + return 0; + +EC_CLEANUP_BEGIN + if (hif != hifInvalid) + { + DeppDisable(hif); + DmgrClose(hif); + } + + ec_print(); + return 1; +EC_CLEANUP_END +} + diff --git a/projects/nexys2_host_controller/host/epp_write.c b/projects/nexys2_host_controller/host/epp_write.c new file mode 100644 index 0000000..707af81 --- /dev/null +++ b/projects/nexys2_host_controller/host/epp_write.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include + +// Digilent SDK +#include "dpcdecl.h" +#include "depp.h" +#include "dmgr.h" + +#include "ec.h" + + +// ----------------------------------------------------------------------------- + + +int main(int argc, char **argv) +{ + long address; + long value; + HIF hif = hifInvalid; + char *device = "Nexys2"; + int32_t port_count; + uint32_t port_properties; + int32_t port = 0; + char version[cchVersionMax]; + + // ----------------------------------------------------------- + // Parse commnd line args + + if (argc != 3) + { + fprintf(stderr, "Wrong number of arguments\n"); + return 1; + } + + address = strtol(argv[1], NULL, 0); // TODO: error handling + value = strtol(argv[2], NULL, 0); // TODO: error handling + + // ----------------------------------------------------------- + // Initilize EPP interface + + // Print DEPP version + EC_FALSE(DeppGetVersion(version)); + //printf("DEPP version: %s\n", version); + + // Open device + EC_FALSE(DmgrOpen(&hif, device)); + //printf("Opened device \"%s\"\n", device); + + // Check number of DEPP ports + EC_FALSE(DeppGetPortCount(hif, &port_count)); + //printf("Port count: %" PRIi32 "\n", port_count); + for (int32_t i = 0; i < port_count; i++) + { + EC_FALSE(DeppGetPortProperties(hif, i, &port_properties)); + //printf("Port %" PRIi32 " properties: 0x%08" PRIx32 "\n", i, port_properties); + } + if (port_count != 1) + { + fprintf(stderr, "TODO: number of DEPP ports: %" PRIi32 "\n", port_count); + EC_FAIL; + } + + // Enable DEPP port + EC_FALSE(DeppEnableEx(hif, port)); + //printf("Opened EPP port %" PRIi32 "\n", port); + + // Set timeout + // uint32_t timeout; + // EC_FALSE(DeppSetTimeout(hif, 0xffffffff, &timeout)); + // printf("Timeout set to %" PRIu32 "ns\n", timeout); + + // ----------------------------------------------------------- + // Write EPP register and report + + EC_FALSE(DeppPutReg(hif, address, (uint8_t)value, false)); + printf("Write: 0x%02x <= 0x%02x\n", (unsigned)address, (unsigned)value); + + // ----------------------------------------------------------- + // Close EPP session + + if (hif != hifInvalid) + { + DeppDisable(hif); + DmgrClose(hif); + //printf("Closed session\n"); + } + + return 0; + +EC_CLEANUP_BEGIN + if (hif != hifInvalid) + { + DeppDisable(hif); + DmgrClose(hif); + } + + ec_print(); + return 1; +EC_CLEANUP_END +} + diff --git a/projects/nexys2_host_controller/host/fileformat.c b/projects/nexys2_host_controller/host/fileformat.c new file mode 100644 index 0000000..e7f3d89 --- /dev/null +++ b/projects/nexys2_host_controller/host/fileformat.c @@ -0,0 +1,270 @@ +#include + +#include "ec.h" + +#include "fileformat.h" + + +// ----------------------------------------------------------------------------- + + +bool iter_next_ihex(struct iter *iter) +{ + assert(iter != NULL); + assert(iter->source != NULL); + assert(iter->format == F_INTELHEX); + + (void)iter; + + return false; +} + + +bool iter_next_srec(struct iter *iter) +{ + assert(iter != NULL); + assert(iter->source != NULL); + assert(iter->format == F_MOTOSREC); + + (void)iter; + + return false; +} + + +bool iter_next_bin(struct iter *iter) +{ + assert(iter != NULL); + assert(iter->source != NULL); + assert(iter->format == F_RAWBIN); + + iter->address += iter->data_len; + iter->data_len = 0; + + iter->data_len = fread(&iter->data[0], 1, MAX_CHUNK, iter->source); + EC_NZ(ferror(iter->source)); + return true; + +EC_CLEANUP_BEGIN + return false; +EC_CLEANUP_END +} + + +bool iter_append_ihex(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len) +{ + assert(iter != NULL); + assert(iter->source != NULL); + assert(data != NULL); + + (void)iter; + (void)addr; + (void)data; + (void)data_len; + + return false; +} + + +bool iter_append_srec(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len) +{ + assert(iter != NULL); + assert(iter->source != NULL); + assert(data != NULL); + + (void)iter; + (void)addr; + (void)data; + (void)data_len; + + return false; +} + + +bool iter_append_bin(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len) +{ + assert(iter != NULL); + assert(iter->source != NULL); + assert(data != NULL); + + // Successive calls must be contiguous for this format + EC_FALSE(iter->address == addr); + EC_NE(fwrite(data, 1, data_len, iter->source), data_len); + EC_NZ(ferror(iter->source)); + iter->address += data_len; + return true; + +EC_CLEANUP_BEGIN + return false; +EC_CLEANUP_END +} + + +bool write_ihex(FILE *f, uint8_t *data, size_t data_len) +{ + assert(f != NULL); + assert(data != NULL); + + (void)f; + (void)data; + (void)data_len; + + return false; +} + + +bool write_srec(FILE *f, uint8_t *data, size_t data_len) +{ + assert(f != NULL); + assert(data != NULL); + + (void)f; + (void)data; + (void)data_len; + + return false; +} + + +bool write_bin(FILE *f, uint8_t *data, size_t data_len) +{ + assert(f != NULL); + assert(data != NULL); + + EC_NE(fwrite(data, 1, data_len, f), data_len); + EC_NZ(ferror(f)); + return true; + +EC_CLEANUP_BEGIN + return false; +EC_CLEANUP_END +} + + +// ----------------------------------------------------------------------------- + + +bool iter_read(struct iter *iter, const char *filename, enum format format) +{ + assert(iter != NULL); + assert(filename != NULL); + assert(format == F_INTELHEX || + format == F_MOTOSREC || + format == F_RAWBIN); + + iter->source = NULL; + iter->format = format; + iter->address = 0; + iter->data_len = 0; + EC_NULL(iter->source = fopen(filename, "r")); + return true; + +EC_CLEANUP_BEGIN + fclose(iter->source); + return false; +EC_CLEANUP_END +} + + +bool iter_write(struct iter *iter, const char *filename, enum format format) +{ + assert(iter != NULL); + assert(filename != NULL); + assert(format == F_INTELHEX || + format == F_MOTOSREC || + format == F_RAWBIN); + + iter->source = NULL; + iter->format = format; + iter->address = 0; + iter->data_len = 0; + EC_NULL(iter->source = fopen(filename, "w")); + return true; + +EC_CLEANUP_BEGIN + fclose(iter->source); + return false; +EC_CLEANUP_END +} + + +void iter_close(struct iter *iter) +{ + fclose(iter->source); + iter->source = NULL; +} + + +bool iter_next(struct iter *iter) +{ + switch (iter->format) + { + case F_INTELHEX: return iter_next_ihex(iter); + case F_MOTOSREC: return iter_next_srec(iter); + case F_RAWBIN: return iter_next_bin(iter); + default: assert(false); return false; + } + assert(false); + return false; +} + + +bool iter_done(struct iter *iter) +{ + assert(iter != NULL); + assert(iter->source != NULL); + + return (bool)feof(iter->source); +} + + +bool iter_append(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len) +{ + assert(iter != NULL); + assert(iter->source != NULL); + assert(data != NULL); + + switch (iter->format) + { + case F_INTELHEX: EC_FALSE(iter_append_ihex(iter, addr, data, data_len)); break; + case F_MOTOSREC: EC_FALSE(iter_append_srec(iter, addr, data, data_len)); break; + case F_RAWBIN: EC_FALSE(iter_append_bin(iter, addr, data, data_len)); break; + default: assert(false); EC_FAIL; break; + } + return true; + +EC_CLEANUP_BEGIN + return false; +EC_CLEANUP_END +} + + +bool write_file(const char *filename, enum format format, uint8_t *data, size_t data_len) +{ + assert(filename != NULL); + assert(format == F_INTELHEX || + format == F_MOTOSREC || + format == F_RAWBIN); + assert(data != NULL); + + FILE *dest = NULL; + + EC_NULL(dest = fopen(filename, "w")); + + switch (format) + { + case F_INTELHEX: EC_FALSE(write_ihex(dest, data, data_len)); break; + case F_MOTOSREC: EC_FALSE(write_srec(dest, data, data_len)); break; + case F_RAWBIN: EC_FALSE(write_bin(dest, data, data_len)); break; + default: assert(false); EC_FAIL; break; + } + + EC_EOF(fclose(dest)); + return true; + +EC_CLEANUP_BEGIN + fclose(dest); + return false; +EC_CLEANUP_END +} + diff --git a/projects/nexys2_host_controller/host/fileformat.h b/projects/nexys2_host_controller/host/fileformat.h new file mode 100644 index 0000000..d3c5c47 --- /dev/null +++ b/projects/nexys2_host_controller/host/fileformat.h @@ -0,0 +1,32 @@ +#pragma once + +#include + + +enum format { F_INTELHEX, F_MOTOSREC, F_RAWBIN }; + + +#define MAX_CHUNK ((size_t)4096) + +struct iter +{ + FILE *source; + enum format format; + + uint32_t address; + size_t data_len; + uint8_t data[MAX_CHUNK]; +}; + + +bool iter_read(struct iter *iter, const char *filename, enum format format); +bool iter_write(struct iter *iter, const char *filename, enum format format); +void iter_close(struct iter *iter); + +// Read streams +bool iter_next(struct iter *iter); +bool iter_done(struct iter *iter); + +// Write streams +bool iter_append(struct iter *iter, uint32_t addr, uint8_t *data, size_t data_len); + diff --git a/projects/nexys2_host_controller/host/memctrl.c b/projects/nexys2_host_controller/host/memctrl.c new file mode 100644 index 0000000..031701f --- /dev/null +++ b/projects/nexys2_host_controller/host/memctrl.c @@ -0,0 +1,361 @@ +#include "ec.h" + +#include "memctrl.h" + +// Digilent SDK +#include +#include +#include + +// ----------------------------------------------------------------------------- + + +// 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 +} + diff --git a/projects/nexys2_host_controller/host/memctrl.h b/projects/nexys2_host_controller/host/memctrl.h new file mode 100644 index 0000000..2b1f1ce --- /dev/null +++ b/projects/nexys2_host_controller/host/memctrl.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +// Defined here to prevent multiple definitions of constants from dpcdecl.h +typedef uint32_t HIF; +#define hifInvalid 0 + + +bool epp_open(HIF *hif_out, char *device, int32_t port); +void epp_close(HIF hif); + +bool ram_read(HIF hif, uint32_t addr, uint8_t *data_out, size_t data_len); +bool ram_write(HIF hif, uint32_t addr, const uint8_t *data, size_t data_len); + +bool flash_read(HIF hif, uint32_t addr, uint8_t *data_out, size_t data_len); +bool flash_cfi(HIF hif, uint32_t addr, uint8_t *data_out, size_t data_len); +bool flash_erase(HIF hif, uint32_t addr); +bool flash_write(HIF hif, uint32_t addr, const uint8_t *data, size_t data_len); + +bool set_exit(HIF hif, bool reset_device); + diff --git a/projects/nexys2_host_controller/host/test.sh b/projects/nexys2_host_controller/host/test.sh new file mode 100755 index 0000000..dea46e0 --- /dev/null +++ b/projects/nexys2_host_controller/host/test.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -e + +echo "Control reg:" +./epp_read 0 +./epp_write 0 0xc0 +./epp_read 0 +echo "Mem ctrl:" +./epp_read 8 +./epp_write 8 0xc0 +echo "Address 0" +./epp_write 0x09 0x00 +./epp_write 0x0a 0x00 +./epp_write 0x0b 0x00 +echo "Write data 0xa5c3" +./epp_write 0x0d 0xa5 +./epp_write 0x0c 0xc3 +echo "Address 2" +./epp_write 0x09 0x02 +./epp_write 0x0a 0x00 +./epp_write 0x0b 0x00 +echo "Write data 0x5a3c" +./epp_write 0x0d 0x5a +./epp_write 0x0c 0x3c +echo "Address 0" +./epp_write 0x09 0x00 +./epp_write 0x0a 0x00 +./epp_write 0x0b 0x00 +echo "Read data" +./epp_read 0x0c +./epp_read 0x0d +echo "Address 2" +./epp_write 0x09 0x02 +./epp_write 0x0a 0x00 +./epp_write 0x0b 0x00 +echo "Read data" +./epp_read 0x0c +./epp_read 0x0d + diff --git a/projects/nexys2_host_controller/nexys2.vhd b/projects/nexys2_host_controller/nexys2.vhd new file mode 100644 index 0000000..0b1d77f --- /dev/null +++ b/projects/nexys2_host_controller/nexys2.vhd @@ -0,0 +1,126 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library nexys2_lib; + + +entity nexys2 is + port ( + clk_50: in std_logic; + + -- EPP interface + EppASTB: in std_logic; + EppDSTB: in std_logic; + EppWRITE: in std_logic; + EppWAIT: out std_logic; + DB: inout std_logic_vector(7 downto 0); + + -- Memory interface + MemOE: out std_logic; + MemWR: out std_logic; + + RamAdv: out std_logic; + RamCS: out std_logic; + RamClk: out std_logic; + RamCRE: out std_logic; + RamLB: out std_logic; + RamUB: out std_logic; + RamWait: in std_logic; + + FlashRp: out std_logic; + FlashCS: out std_logic; + FlashStSts: in std_logic; + + MemAdr: out std_logic_vector(23 downto 1); + MemDB: inout std_logic_vector(15 downto 0); + + -- 7-segment display + seg: out std_logic_vector(6 downto 0); + dp: out std_logic; + an: out std_logic_vector(3 downto 0); + + -- Misc + Led: out std_logic_vector(7 downto 0); + sw: in std_logic_vector(7 downto 0) + ); +end nexys2; + + +architecture behavioral of nexys2 is + + type regs8 is array(natural range <>) of std_logic_vector(7 downto 0); + + -- Wishbone SYSCON signals + signal clk: std_logic; + signal rst: std_logic; + +begin + + -- Wishbone SYSCON + clk <= clk_50; + rst <= sw(7); + + e_host_ctrl: entity nexys2_lib.host_ctrl + port map ( + clk_i => clk, + rst_i => rst, + + d_rst_o => open, + d_flags_o => open, + debug_i => x"8877665544332211", + debug_o => open, + + d_cyc_i => '0', + d_stb_i => '0', + d_we_i => '0', + d_ack_o => open, + d_adr_i => (others => '0'), + d_dat_i => (others => '0'), + d_dat_o => open, + + d_MemOE => '1', + d_MemWR => '1', + d_RamAdv => '1', + d_RamCS => '1', + d_RamClk => '0', + d_RamCRE => '0', + d_RamLB => '1', + d_RamUB => '1', + d_RamWait => open, + d_FlashRp => '1', + d_FlashCS => '1', + d_FlashStSts => open, + d_MemAdr => (others => '0'), + d_MemDB_i => open, + d_MemDB_o => (others => '0'), + + EppAstb => EppAstb, + EppDstb => EppDstb, + EppWr => EppWRITE, + EppDB => DB, + EppWait => EppWait, + + MemOE => MemOE, + MemWR => MemWR, + RamAdv => RamAdv, + RamCS => RamCS, + RamClk => RamClk, + RamCRE => RamCRE, + RamLB => RamLB, + RamUB => RamUB, + RamWait => RamWait, + FlashRp => FlashRp, + FlashCS => FlashCS, + FlashStSts => FlashStSts, + MemAdr => MemAdr, + MemDB => MemDB, + + seg => seg, + dp => dp, + an => an, + Led => Led, + sw => sw + ); + +end behavioral; diff --git a/projects/nexys2_host_controller/testbench_hostctrl.vhd b/projects/nexys2_host_controller/testbench_hostctrl.vhd new file mode 100644 index 0000000..e191a27 --- /dev/null +++ b/projects/nexys2_host_controller/testbench_hostctrl.vhd @@ -0,0 +1,208 @@ +library ieee; +use ieee.std_logic_1164.all; + +library simulated; + +library work; + + +entity testbench_hostctrl is +end testbench_hostctrl; + + +architecture behavior of testbench_hostctrl is + + constant clk_50_period : time := 20 ns; + + signal clk_50: std_logic := '0'; + + signal EppASTB: std_logic := '0'; + signal EppDSTB: std_logic := '0'; + signal EppWRITE: std_logic := '0'; + signal EppWAIT: std_logic; + signal EppDB: std_logic_vector(7 downto 0); + + signal RamWait: std_logic := '0'; + signal FlashStSts: std_logic := '0'; + + signal RamAdv: std_logic; + signal RamCS: std_logic; + signal RamClk: std_logic; + signal RamCRE: std_logic; + signal RamLB: std_logic; + signal RamUB: std_logic; + + signal FlashRp: std_logic; + signal FlashCS: std_logic; + + signal MemOE: std_logic; + signal MemWR: std_logic; + signal MemAdr: std_logic_vector(23 downto 1); + signal MemDB: std_logic_vector(15 downto 0); + + signal sw: std_logic_vector(7 downto 0) := (others => '0'); + signal Led: std_logic_vector(7 downto 0); + +begin + + test: process + begin + -- Initial values + EppDB <= (others => 'Z'); + EppASTB <= '1'; + EppDSTB <= '1'; + EppWRITE <= '1'; + + -- Reset switch + sw <= (7 => '1', others => '0'); + wait for 100 ns; + sw <= (others => '0'); + + -- 0x10 <= 0xa5 + EppDB <= x"10"; + EppASTB <= '0'; + EppWRITE <= '0'; + wait until EppWAIT = '1'; + eppDB <= (others => 'Z'); + EppASTB <= '1'; + EppWRITE <= '1'; + wait until EppWAIT = '0'; + EppDB <= x"a5"; + EppDSTB <= '0'; + EppWRITE <= '0'; + wait until EppWAIT = '1'; + EppDB <= (others => 'Z'); + EppDSTB <= '1'; + EppWRITE <= '1'; + wait until EppWAIT = '0'; + + -- 0x00 <= 0xc0 + -- Enable OOB memory controller and reset device + EppDB <= x"00"; + EppASTB <= '0'; + EppWRITE <= '0'; + wait until EppWAIT = '1'; + EppDB <= (others => 'Z'); + EppASTB <= '1'; + EppWRITE <= '1'; + wait until EppWAIT = '0'; + EppDB <= x"c0"; + EppDSTB <= '0'; + EppWRITE <= '0'; + wait until EppWAIT = '1'; + EppDB <= (others => 'Z'); + EppDSTB <= '1'; + EppWRITE <= '1'; + wait until EppWAIT = '0'; + + -- 0x08 <= 0x08 + -- Enable flash and disable ram + EppDB <= x"08"; + EppASTB <= '0'; + EppWRITE <= '0'; + wait until EppWAIT = '1'; + EppDB <= (others => 'Z'); + EppASTB <= '1'; + EppWRITE <= '1'; + wait until EppWAIT = '0'; + EppDB <= x"08"; + EppDSTB <= '0'; + EppWRITE <= '0'; + wait until EppWAIT = '1'; + EppDB <= (others => 'Z'); + EppDSTB <= '1'; + EppWRITE <= '1'; + wait until EppWAIT = '0'; + + -- 0x0c <= 0x98 + -- CFI query command + EppDB <= x"0c"; + EppASTB <= '0'; + EppWRITE <= '0'; + wait until EppWAIT = '1'; + EppDB <= (others => 'Z'); + EppASTB <= '1'; + EppWRITE <= '1'; + wait until EppWAIT = '0'; + EppDB <= x"98"; + EppDSTB <= '0'; + EppWRITE <= '0'; + wait until EppWAIT = '1'; + EppDB <= (others => 'Z'); + EppDSTB <= '1'; + EppWRITE <= '1'; + wait until EppWAIT = '0'; + + wait; + end process; + + + uut: entity work.nexys2 + port map ( + clk_50 => clk_50, + EppASTB => EppASTB, + EppDSTB => EppDSTB, + EppWRITE => EppWRITE, + EppWAIT => EppWAIT, + DB => EppDB, + MemOE => MemOE, + MemWR => MemWR, + RamAdv => RamAdv, + RamCS => RamCS, + RamClk => RamClk, + RamCRE => RamCRE, + RamLB => RamLB, + RamUB => RamUB, + RamWait => RamWait, + FlashRp => FlashRp, + FlashCS => FlashCS, + FlashStSts => FlashStSts, + MemAdr => MemAdr, + MemDB => MemDB, + seg => open, + dp => open, + an => open, + Led => Led, + sw => sw + ); + + + ram: entity simulated.mt45w8mw16bgx + port map ( + a => MemAdr, + dq => MemDB, + clk => RamClk, + adv_n => RamAdv, + cre => RamCRE, + ce_n => RamCS, + oe_n => MemOE, + we_n => MemWR, + lb_n => RamLB, + ub_n => RamUB, + rwait => RamWait + ); + + + flash: entity simulated.js28f128j3d75 + port map ( + a => MemAdr & '0', + d => MemDB, + ce => "00" & FlashCS, + rp_n => FlashRp, + oe_n => MemOE, + we_n => MemWR, + sts => FlashStSts, + byte_n => '1', + vpen => '1' + ); + + + clk_50_process: process + begin + clk_50 <= '0'; + wait for clk_50_period / 2; + clk_50 <= '1'; + wait for clk_50_period / 2; + end process; + +end;