Interfacing an SNES controller with an FPGA (help)

Discussion in 'Embedded Systems and Microcontrollers' started by aftrumpet, Jan 7, 2015.

  1. aftrumpet

    Thread Starter New Member

    Jan 7, 2015
    6
    0
    I have been trying to write a Verilog design for a while to interface with a SNES controller and read it's button outputs, using this page as a guide: http://www.gamefaqs.com/snes/916396-super-nintendo/faqs/5395

    This is what I have:
    Code (Text):
    1. module SNES(input clock, input snes_data,
    2.     output snes_clock, output reg snes_latch,
    3.     output reg a, output reg b, output reg x, output reg y,
    4.     output reg up, output reg down, output reg left, output reg right,
    5.     output reg l, output reg r, output reg start, output reg select);
    6.  
    7. reg[3:0] btn_counter;
    8.  
    9. reg latch;
    10.  
    11. initial latch <= 1;
    12.  
    13. assign snes_clock = snes_latch ? 1 : clock;
    14.  
    15. always @ (posedge clock)
    16. snes_latch <= latch;
    17.  
    18. always @ (negedge clock)
    19. if (latch) latch <= 0;
    20. else begin
    21. case (btn_counter)
    22.     0: b <= ~snes_data;
    23.     1: y <= ~snes_data;
    24.     2: select <= ~snes_data;
    25.     3: start <= ~snes_data;
    26.     4: up <= ~snes_data;
    27.     5: down <= ~snes_data;
    28.     6: left <= ~snes_data;
    29.     7: right <= ~snes_data;
    30.     8: a <= ~snes_data;
    31.     9: x <= ~snes_data;
    32.     10: l <= ~snes_data;
    33.     11: r <= ~snes_data;
    34. endcase
    35. btn_counter <= btn_counter + 1;
    36. latch <= btn_counter == 0;
    37. end
    38.  
    39. endmodule
    And the clock I'm using has a period of 12us (to fit the specifications described), and is divided up with this:
    Code (Text):
    1. module clk_div(Clk_in, Clk_out);
    2.  
    3. // input ports
    4. input Clk_in;
    5.  
    6. // output ports
    7. output reg Clk_out;
    8.  
    9. parameter max = 300; // max-counter size
    10.  
    11. reg [8:0]counter = 0; // 9-bit counter size
    12.  
    13. always@(posedge Clk_in) begin
    14.     if (counter == max-1)
    15.         begin
    16.         counter <= 0;
    17.         Clk_out <= ~Clk_out;
    18.         end
    19.     else
    20.         begin
    21.         counter <= counter + 1'd1;
    22.         end
    23.     end
    24. endmodule
    However, I am getting nothing but a high signal from the SNES data line. I have debugged this with both a simulator and Signal Tap and seem to be doing everything right. My logic analyzer output is attached, with data, clock, and latch from top to bottom. Just looking at this, can anyone see if I am doing something wrong? This far, I haven't been able to get anything but a high data signal from the controller.
     
  2. JWHassler

    Member

    Sep 25, 2013
    201
    33
    What device are you compiling this for?
    I ask because the construction
    is not supported by most hardware accessible to us (forgive me) small-timers.
    Most parts from A**** and X***** do everything on the positive clock-edge.
    It's not a problem, though: do everything based on your 25MHz(?) input-clock. The value of a particular input is then read at the " positive-edge of the 50th 25MHz clock in a 300 clock sequence" or
    whatever the numbers work out to.
    Also, I don't see that your clock-divider module is tied to your decoder in any way. Perhaps you have a higher-level module that connects them?
     
  3. aftrumpet

    Thread Starter New Member

    Jan 7, 2015
    6
    0
    I probably should have explained this clearer. I'm using a block diagram to connect the output of the clock divider to the clock of the SNES controller module.

    As for hardware, I'm using the Altera DE0 board, which is powered by a Cyclone III. According to the attached Signal Tap output, the negative edge trigger is working as expected and the signals are what I want them to be, but the SNES controller is not responding as it should so I think my methodology is wrong, not the execution.
     
  4. Brownout

    Well-Known Member

    Jan 10, 2012
    2,375
    998
    If you mean "snes_data" you've declared that as an input, and you don't generate that with your code. There is nothing you can do from a code standpoint to fix this. What is this signal? Where is it generated? If this comes from an external device, check that your design is pinned out correctly. BTW, you need to connect the 'reset' signal from your board and use it to initialize your logic. The "initial" construct doesn't work for read hardware.
     
    Last edited: Jan 8, 2015
  5. Brownout

    Well-Known Member

    Jan 10, 2012
    2,375
    998
    One more thing, it looks like you're generating the snes_latch in your code, but if that is a signal to synchronize the arriving data, it must come from the external device.
     
  6. MagicMatt

    Member

    Sep 30, 2013
    117
    14
    No, the latch is created by the console, and is a signal to tell the controller to read the state of the buttons. When you release latch, the controller then sends back the button data.
     
  7. MagicMatt

    Member

    Sep 30, 2013
    117
    14
    The clock is for reading the button data... I wouldn't regard it as a clock personally, it's more like a "next" signal. When you release the latch, you get the state of the first button on the data line. If you then pulse the next(clock) line, you get the second button on the data line, etc.
     
  8. aftrumpet

    Thread Starter New Member

    Jan 7, 2015
    6
    0
    snes_data is an input, yes, but it comes from the SNES controller and is controlled by the latch and clock lines. I have tested an Arduino library someone made for this and it does the same thing, the code can be viewed here: https://github.com/TacticalCode/SNESpaduino/blob/master/SNESpaduino.cpp

    As far as finding a solution goes, one potential source of the problem is that the GPIO pins on the DE0 board are 3.3V signals, but I have been providing 5V power to the SNES controller. I can try to power it with 3.3V and see if this solves anything, and if this doesn't work I may have to use a logic level converter.

    I am also going to try to analyze the signals from the working Arduino sketch and potentially replicate those.
     
  9. MagicMatt

    Member

    Sep 30, 2013
    117
    14
    I can't remember what the actual chips are inside the SNES controllers - whether they are CMOS or TTL. If they are CMOS, you may be creating your own problem, because the "high" level will be 2/3 of VDD, and at 5V that's 3V, which is pretty close to the 3.3V and doesn't give you much allowance for any drop on the cable etc. If you supply 3,3V to the controller and it's CMOS, it should run ok and the logic high will drop down to just under 2V.
     
  10. aftrumpet

    Thread Starter New Member

    Jan 7, 2015
    6
    0
    Just looked it up and the controller is indeed CMOS. I tried using the FPGA to analyze the Arduino's logic signals but those are 5V, so no luck there. I ordered a logic analyzer though so that should come next week.

    Also of note is I tried unconnecting the power line to the controller and still got a high data signal, meaning that it's just the default pulled up high and nothing more. Based on this I might have my pin configuration wrong, though I have checked many times to ensure that it's correct.
     
  11. MagicMatt

    Member

    Sep 30, 2013
    117
    14
    I found an old SNES controller... I couldn't use your code as I only have MASM and XC8...

    This sequence worked though, based on your link in psuedo-code:
    Code (Text):
    1. Put the "latch" pin high for 12 micro seconds
    2. Put the "latch" pin low
    3. LOOP 12 times:
    4.     Wait 6 micro seconds
    5.     Read the serial data line (gives you status of button)
    6.     Wait 6 micro seconds
    7.     Put "clock" to high
    8.     Wait 12 micro seconds
    9.     Put "clock" to low
    10. END LOOP
    11.  
    Every time you go around the loop, you get the status of the next button until you've read all the buttons.

    You can use a timer to make that execute every 16mS (the spec says 16.67mS but it happily ran far more frequently).
     
  12. MagicMatt

    Member

    Sep 30, 2013
    117
    14
    Note: It will also run far faster - this just worked too:
    Code (Text):
    1.  
    2. Put the "latch" pin high for 12 micro seconds
    3. Put the "latch" pin low
    4. LOOP 12 times:
    5.     Wait 3 micro seconds
    6.     Read the serial data line (gives you status of button)
    7.     Wait 3 micro seconds
    8.     Put "clock" to high
    9.     Wait 6 micro seconds
    10.     Put "clock" to low
    11. END LOOP
    12.  
     
  13. aftrumpet

    Thread Starter New Member

    Jan 7, 2015
    6
    0
    Can you look at the logic analyzer output I've attached to the first post? That seems like exactly what I've been doing, and if this is the same then I likely have a connection or voltage issue.
     
  14. MagicMatt

    Member

    Sep 30, 2013
    117
    14
    I see two differences (other than the lack of data)
    1 - I used a cheap PIC I had laying around - a PIC16F690 - everything is 5V.
    2 - My clock is LOW and STAYS LOW when I trigger the latch. This is why I do not like calling it a clock - it's a "NEXT" - a trigger to read the next value on a 16 bit shift register.
     
  15. MagicMatt

    Member

    Sep 30, 2013
    117
    14
    As far as I can tell, what happens inside the controller is that the "latch" tells the controller to read the button states and load them into the 16bit shift register (probably two 8 bit registers in series). You're then reading that data and using the "next" signal to shuffle the data along to read the next value.
     
  16. aftrumpet

    Thread Starter New Member

    Jan 7, 2015
    6
    0
    I just tested the circuit with my logic analyzer, and I'm not getting anything on the output pins. Weird. My DE0's GPIO may be broken (though I don't know when that would've happened), but I also have a Papilio One that I will try this design out on.
     
Loading...