VHDL - 使用 FPGA 通过控制器端口的 SNES 接口
VHDL - SNES Interface via controller port using FPGA
我正在努力尝试将便宜的 FPGA(ep2c5t144 Altera Cyclone II 迷你板)与 SNES 连接起来,以充当 SNES 控制器。到目前为止,它似乎可以打开和关闭...当前的问题是,它在打开后工作了大约 1 秒...但随后似乎卡在了一个状态,直到它被重置。
由于我花了很长时间查看逻辑问题的代码,我开始怀疑这是否是使用 FPGA 的一些奇怪的怪癖,但我已经尝试测试任何不是的状态已定义,但这并没有解决问题。我将 post 下面的 SNES 代码,以及显示问题的廉价逻辑分析器的输出。警告,代码很乱……尤其是我改变了一些东西试图修复它。非常感谢任何想法!
非常感谢您的帮助!
逻辑分析仪的问题:
When a request works - State transitions occur as expected
When a request fails - SEEMS to incorrectly transition directly to "working" state and get stuck for some reason
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity snes_controller is
generic (
hp : integer := 300
);
port (
clk : in std_logic;
latch : in std_logic;
data : out std_logic := '0';
clock : in std_logic;
enable : in std_logic;
btn_B : in std_logic;
btn_Y : in std_logic;
btn_select : in std_logic;
btn_start : in std_logic;
btn_up : in std_logic;
btn_down : in std_logic;
btn_left : in std_logic;
btn_right : in std_logic;
btn_A : in std_logic;
btn_X : in std_logic;
btn_L : in std_logic;
btn_R : in std_logic;
helpA : out std_logic := '0';
helpB : out std_logic := '0';
helpC : out std_logic := '0';
helpD : out std_logic := '0';
helpE : out std_logic := '0'
);
end entity;
architecture Behav of snes_controller is
signal buttons : unsigned(16 downto 0) := "10000000000000000";
type state_type is (s_idle, s_latching_1, s_latching_2, s_working);
signal state : state_type := s_idle;
type cycle_type is (c_high, c_low);
signal cycle : cycle_type := c_high;
begin
process (clk)
variable i : integer range 0 to 16;
variable count : integer range 0 to hp;
begin
if(rising_edge(clk)) then
data <= not buttons(i);
if(state = s_latching_1 or state = s_latching_2 or state = s_working) then
if(count < hp) then
count := count+1;
else
count := 0;
if(state = s_latching_1) then
if(latch = '1') then
state <= s_latching_2;
buttons(0) <= btn_B;
buttons(1) <= btn_Y;
buttons(2) <= btn_select;
buttons(3) <= btn_start;
buttons(4) <= btn_up;
buttons(5) <= btn_down;
buttons(6) <= btn_left;
buttons(7) <= btn_right;
buttons(8) <= btn_A;
buttons(9) <= btn_X;
buttons(10) <= btn_L;
buttons(11) <= btn_R;
else
state <= s_idle;
end if;
elsif(state = s_latching_2) then
state <= s_working;
i := 0;
cycle <= c_high;
elsif(state = s_working) then
if(latch = '1') then
state <= s_idle;
helpD <= '1';
elsif(cycle = c_low) then
cycle <= c_high;
if(i < 16) then
i := i+1;
else
state <= s_idle;
helpD <= '0';
helpE <= '0';
end if;
else
cycle <= c_low;
end if;
end if;
end if;
elsif(state = s_idle) then
if(latch = '1') then
state <= s_latching_1;
count := 0;
i := 0;
end if;
else
helpE <= '1';
state <= s_idle;
count := 0;
i := 0;
end if;
end if;
end process;
process(state)
begin
if(state = s_idle) then
helpA <= '0';
helpB <= '0';
elsif(state = s_latching_1) then
helpA <= '1';
helpB <= '0';
elsif(state = s_latching_2) then
helpA <= '0';
helpB <= '1';
elsif(state = s_working) then
helpA <= '1';
helpB <= '1';
else
helpA <= clk;
helpB <= not clk;
end if;
if(cycle = c_low) then
helpC <= '0';
elsif(cycle = c_high) then
helpC <= '1';
end if;
end process;
end Behav;
您正在使用异步外部输入并将它们馈送到基于时钟的同步状态机。采样中的亚稳定性可能会导致您的问题。确保为每个输入信号至少实现一个双触发器同步器。
在此处阅读更多相关信息:http://webee.technion.ac.il/~ran/papers/Metastability%20and%20Synchronizers.posted.pdf
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity snes_controller is
generic (
hp : integer := 300
);
port (
clk : in std_logic;
latch : in std_logic;
data : out std_logic := '0';
clock : in std_logic;
enable : in std_logic;
btn_B : in std_logic;
btn_Y : in std_logic;
btn_select : in std_logic;
btn_start : in std_logic;
btn_up : in std_logic;
btn_down : in std_logic;
btn_left : in std_logic;
btn_right : in std_logic;
btn_A : in std_logic;
btn_X : in std_logic;
btn_L : in std_logic;
btn_R : in std_logic;
helpA : out std_logic := '0';
helpB : out std_logic := '0';
helpC : out std_logic := '0';
helpD : out std_logic := '0';
helpE : out std_logic := '0'
);
end entity;
architecture Behav of snes_controller is
signal synch0 : unsigned(11 downto 0) := (others => '0');
signal synch1 : unsigned(11 downto 0) := (others => '0');
signal synch2 : unsigned(11 downto 0) := (others => '0');
signal buttons : unsigned(16 downto 0) := "10000000000000000";
type state_type is (s_idle, s_latching_1, s_latching_2, s_working);
signal state : state_type := s_idle;
type cycle_type is (c_high, c_low);
signal cycle : cycle_type := c_high;
begin
process (clk)
variable i : integer range 0 to 16;
variable count : integer range 0 to hp;
begin
if(rising_edge(clk)) then
synch0(0) <= btn_B;
synch0(1) <= btn_Y;
synch0(2) <= btn_select;
synch0(3) <= btn_start;
synch0(4) <= btn_up;
synch0(5) <= btn_down;
synch0(6) <= btn_left;
synch0(7) <= btn_right;
synch0(8) <= btn_A;
synch0(9) <= btn_X;
synch0(10) <= btn_L;
synch0(11) <= btn_R;
synch1 <= synch0;
synch2 <= synch1;
data <= not buttons(i);
if(state = s_latching_1 or state = s_latching_2 or state = s_working) then
if(count < hp) then
count := count+1;
else
count := 0;
if(state = s_latching_1) then
if(latch = '1') then
state <= s_latching_2;
buttons(11 downto 0) <= synch2(11 downto 0);
else
state <= s_idle;
end if;
elsif(state = s_latching_2) then
state <= s_working;
i := 0;
cycle <= c_high;
elsif(state = s_working) then
if(latch = '1') then
state <= s_idle;
helpD <= '1';
elsif(cycle = c_low) then
cycle <= c_high;
if(i < 16) then
i := i+1;
else
state <= s_idle;
helpD <= '0';
helpE <= '0';
end if;
else
cycle <= c_low;
end if;
end if;
end if;
elsif(state = s_idle) then
if(latch = '1') then
state <= s_latching_1;
count := 0;
i := 0;
end if;
else
helpE <= '1';
state <= s_idle;
count := 0;
i := 0;
end if;
end if;
end process;
process(state)
begin
if(state = s_idle) then
helpA <= '0';
helpB <= '0';
elsif(state = s_latching_1) then
helpA <= '1';
helpB <= '0';
elsif(state = s_latching_2) then
helpA <= '0';
helpB <= '1';
elsif(state = s_working) then
helpA <= '1';
helpB <= '1';
else
helpA <= clk;
helpB <= not clk;
end if;
if(cycle = c_low) then
helpC <= '0';
elsif(cycle = c_high) then
helpC <= '1';
end if;
end process;
end Behav;
此外,我建议创建某种过滤器来处理按钮点击的去抖动。 http://www.eng.utah.edu/~cs5780/debouncing.pdf
我正在努力尝试将便宜的 FPGA(ep2c5t144 Altera Cyclone II 迷你板)与 SNES 连接起来,以充当 SNES 控制器。到目前为止,它似乎可以打开和关闭...当前的问题是,它在打开后工作了大约 1 秒...但随后似乎卡在了一个状态,直到它被重置。
由于我花了很长时间查看逻辑问题的代码,我开始怀疑这是否是使用 FPGA 的一些奇怪的怪癖,但我已经尝试测试任何不是的状态已定义,但这并没有解决问题。我将 post 下面的 SNES 代码,以及显示问题的廉价逻辑分析器的输出。警告,代码很乱……尤其是我改变了一些东西试图修复它。非常感谢任何想法!
非常感谢您的帮助!
逻辑分析仪的问题:
When a request works - State transitions occur as expected
When a request fails - SEEMS to incorrectly transition directly to "working" state and get stuck for some reason
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity snes_controller is
generic (
hp : integer := 300
);
port (
clk : in std_logic;
latch : in std_logic;
data : out std_logic := '0';
clock : in std_logic;
enable : in std_logic;
btn_B : in std_logic;
btn_Y : in std_logic;
btn_select : in std_logic;
btn_start : in std_logic;
btn_up : in std_logic;
btn_down : in std_logic;
btn_left : in std_logic;
btn_right : in std_logic;
btn_A : in std_logic;
btn_X : in std_logic;
btn_L : in std_logic;
btn_R : in std_logic;
helpA : out std_logic := '0';
helpB : out std_logic := '0';
helpC : out std_logic := '0';
helpD : out std_logic := '0';
helpE : out std_logic := '0'
);
end entity;
architecture Behav of snes_controller is
signal buttons : unsigned(16 downto 0) := "10000000000000000";
type state_type is (s_idle, s_latching_1, s_latching_2, s_working);
signal state : state_type := s_idle;
type cycle_type is (c_high, c_low);
signal cycle : cycle_type := c_high;
begin
process (clk)
variable i : integer range 0 to 16;
variable count : integer range 0 to hp;
begin
if(rising_edge(clk)) then
data <= not buttons(i);
if(state = s_latching_1 or state = s_latching_2 or state = s_working) then
if(count < hp) then
count := count+1;
else
count := 0;
if(state = s_latching_1) then
if(latch = '1') then
state <= s_latching_2;
buttons(0) <= btn_B;
buttons(1) <= btn_Y;
buttons(2) <= btn_select;
buttons(3) <= btn_start;
buttons(4) <= btn_up;
buttons(5) <= btn_down;
buttons(6) <= btn_left;
buttons(7) <= btn_right;
buttons(8) <= btn_A;
buttons(9) <= btn_X;
buttons(10) <= btn_L;
buttons(11) <= btn_R;
else
state <= s_idle;
end if;
elsif(state = s_latching_2) then
state <= s_working;
i := 0;
cycle <= c_high;
elsif(state = s_working) then
if(latch = '1') then
state <= s_idle;
helpD <= '1';
elsif(cycle = c_low) then
cycle <= c_high;
if(i < 16) then
i := i+1;
else
state <= s_idle;
helpD <= '0';
helpE <= '0';
end if;
else
cycle <= c_low;
end if;
end if;
end if;
elsif(state = s_idle) then
if(latch = '1') then
state <= s_latching_1;
count := 0;
i := 0;
end if;
else
helpE <= '1';
state <= s_idle;
count := 0;
i := 0;
end if;
end if;
end process;
process(state)
begin
if(state = s_idle) then
helpA <= '0';
helpB <= '0';
elsif(state = s_latching_1) then
helpA <= '1';
helpB <= '0';
elsif(state = s_latching_2) then
helpA <= '0';
helpB <= '1';
elsif(state = s_working) then
helpA <= '1';
helpB <= '1';
else
helpA <= clk;
helpB <= not clk;
end if;
if(cycle = c_low) then
helpC <= '0';
elsif(cycle = c_high) then
helpC <= '1';
end if;
end process;
end Behav;
您正在使用异步外部输入并将它们馈送到基于时钟的同步状态机。采样中的亚稳定性可能会导致您的问题。确保为每个输入信号至少实现一个双触发器同步器。
在此处阅读更多相关信息:http://webee.technion.ac.il/~ran/papers/Metastability%20and%20Synchronizers.posted.pdf
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity snes_controller is
generic (
hp : integer := 300
);
port (
clk : in std_logic;
latch : in std_logic;
data : out std_logic := '0';
clock : in std_logic;
enable : in std_logic;
btn_B : in std_logic;
btn_Y : in std_logic;
btn_select : in std_logic;
btn_start : in std_logic;
btn_up : in std_logic;
btn_down : in std_logic;
btn_left : in std_logic;
btn_right : in std_logic;
btn_A : in std_logic;
btn_X : in std_logic;
btn_L : in std_logic;
btn_R : in std_logic;
helpA : out std_logic := '0';
helpB : out std_logic := '0';
helpC : out std_logic := '0';
helpD : out std_logic := '0';
helpE : out std_logic := '0'
);
end entity;
architecture Behav of snes_controller is
signal synch0 : unsigned(11 downto 0) := (others => '0');
signal synch1 : unsigned(11 downto 0) := (others => '0');
signal synch2 : unsigned(11 downto 0) := (others => '0');
signal buttons : unsigned(16 downto 0) := "10000000000000000";
type state_type is (s_idle, s_latching_1, s_latching_2, s_working);
signal state : state_type := s_idle;
type cycle_type is (c_high, c_low);
signal cycle : cycle_type := c_high;
begin
process (clk)
variable i : integer range 0 to 16;
variable count : integer range 0 to hp;
begin
if(rising_edge(clk)) then
synch0(0) <= btn_B;
synch0(1) <= btn_Y;
synch0(2) <= btn_select;
synch0(3) <= btn_start;
synch0(4) <= btn_up;
synch0(5) <= btn_down;
synch0(6) <= btn_left;
synch0(7) <= btn_right;
synch0(8) <= btn_A;
synch0(9) <= btn_X;
synch0(10) <= btn_L;
synch0(11) <= btn_R;
synch1 <= synch0;
synch2 <= synch1;
data <= not buttons(i);
if(state = s_latching_1 or state = s_latching_2 or state = s_working) then
if(count < hp) then
count := count+1;
else
count := 0;
if(state = s_latching_1) then
if(latch = '1') then
state <= s_latching_2;
buttons(11 downto 0) <= synch2(11 downto 0);
else
state <= s_idle;
end if;
elsif(state = s_latching_2) then
state <= s_working;
i := 0;
cycle <= c_high;
elsif(state = s_working) then
if(latch = '1') then
state <= s_idle;
helpD <= '1';
elsif(cycle = c_low) then
cycle <= c_high;
if(i < 16) then
i := i+1;
else
state <= s_idle;
helpD <= '0';
helpE <= '0';
end if;
else
cycle <= c_low;
end if;
end if;
end if;
elsif(state = s_idle) then
if(latch = '1') then
state <= s_latching_1;
count := 0;
i := 0;
end if;
else
helpE <= '1';
state <= s_idle;
count := 0;
i := 0;
end if;
end if;
end process;
process(state)
begin
if(state = s_idle) then
helpA <= '0';
helpB <= '0';
elsif(state = s_latching_1) then
helpA <= '1';
helpB <= '0';
elsif(state = s_latching_2) then
helpA <= '0';
helpB <= '1';
elsif(state = s_working) then
helpA <= '1';
helpB <= '1';
else
helpA <= clk;
helpB <= not clk;
end if;
if(cycle = c_low) then
helpC <= '0';
elsif(cycle = c_high) then
helpC <= '1';
end if;
end process;
end Behav;
此外,我建议创建某种过滤器来处理按钮点击的去抖动。 http://www.eng.utah.edu/~cs5780/debouncing.pdf