Skip to content

Commit

Permalink
Low-speed game gamepad support (#23)
Browse files Browse the repository at this point in the history
Add support for 2 usb gamepads based on hi631's ukp code.
Add doc/usb_gamepad.md.
  • Loading branch information
nand2mario authored Aug 19, 2023
1 parent d4f928d commit adcb3cc
Show file tree
Hide file tree
Showing 16 changed files with 1,426 additions and 40 deletions.
Binary file added doc/images/usb_gamepad1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/usb_gamepad2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions doc/usb_gamepad.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

## USB Gamepad Setup

NESTang 0.6 now supports USB gamepads, allowing you to use your existing controllers without the need to purchase new ones.

<img src="images/usb_gamepad2.jpg" width=400>
<img src="images/usb_gamepad1.jpg" width=400>

To enable this functionality, you will need the following,
* Two USB-A Female to 2.54mm adapters, which can be found [here](https://www.aliexpress.us/item/2255800203914149.html?spm=a2g0o.productlist.main.17.6e617e229i3qAm&algo_pvid=89ee64ce-a2c8-41f6-9e3b-45e8396569fd&algo_exp_id=89ee64ce-a2c8-41f6-9e3b-45e8396569fd-8&pdp_npi=4%40dis%21USD%210.28%210.25%21%21%210.28%21%21%402132a25516924371147167093ec531%2110000001592482118%21sea%21US%214484896846%21A&curPageLogUid=dAeFgl6FWDAf).
* Four 15K ohm resistors as USB pulldown resistor.

Then wire things up correctly, refer to the image above and the [Tang Nano 20K pinout](https://wiki.sipeed.com/hardware/en/tang/tang-nano-20k/nano-20k.html)). Follow these steps,
* Connect USB VBUS to the 5V pin of Tang Nano 20K, and USB GND to the Tang GND.
* For controller 1, connect D+ to pin 42 and D- to pin 41.
* For controller 2, connect D+ to pin 56 and D- to pin 54.
* Connect four 15K ohm resistors from D-/D+ to GND.

Please note that using the resistors is necessary for stability.

That's all you need to do.

**Limitation**: Note that only USB low-speed gamepads are currently supported. So controllers like PS5 or Xbox 360 pads are not compatible.

3 changes: 3 additions & 0 deletions nes.gprj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<File path="src/gowin_clkdiv/gowin_clkdiv.v" type="file.verilog" enable="1"/>
<File path="src/gowin_rpll_hdmi/gowin_rpll.v" type="file.verilog" enable="1"/>
<File path="src/gowin_rpll_nes/gowin_rpll.v" type="file.verilog" enable="1"/>
<File path="src/gowin_rpll_usb.v" type="file.verilog" enable="1"/>
<File path="src/hdmi2/audio_clock_regeneration_packet.sv" type="file.verilog" enable="1"/>
<File path="src/hdmi2/audio_info_frame.sv" type="file.verilog" enable="1"/>
<File path="src/hdmi2/audio_sample_packet.sv" type="file.verilog" enable="1"/>
Expand All @@ -39,6 +40,8 @@
<File path="src/sdcmd_ctrl.sv" type="file.verilog" enable="1"/>
<File path="src/sdram.v" type="file.verilog" enable="1"/>
<File path="src/uart_tx_V2.v" type="file.verilog" enable="1"/>
<File path="src/usb_gamepad.v" type="file.verilog" enable="1"/>
<File path="src/usb_gamepad_rom.v" type="file.verilog" enable="1"/>
<File path="src/nestang.cst" type="file.cst" enable="1"/>
<File path="src/nestang_dock.cst" type="file.cst" enable="0"/>
<File path="src/nestang.sdc" type="file.sdc" enable="1"/>
Expand Down
2 changes: 1 addition & 1 deletion src/gowin_rpll_hdmi/gowin_rpll.v
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,6 @@ defparam rpll_inst.CLKOUTD_BYPASS = "false";
defparam rpll_inst.DYN_SDIV_SEL = 2;
defparam rpll_inst.CLKOUTD_SRC = "CLKOUT";
defparam rpll_inst.CLKOUTD3_SRC = "CLKOUT";
defparam rpll_inst.DEVICE = "GW2A-18C";
defparam rpll_inst.DEVICE = "GW2AR-18";

endmodule //Gowin_rPLL
2 changes: 1 addition & 1 deletion src/gowin_rpll_nes/gowin_rpll.v
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,6 @@ defparam rpll_inst.CLKOUTD_BYPASS = "false";
defparam rpll_inst.DYN_SDIV_SEL = 2;
defparam rpll_inst.CLKOUTD_SRC = "CLKOUT";
defparam rpll_inst.CLKOUTD3_SRC = "CLKOUT";
defparam rpll_inst.DEVICE = "GW2AR-18C";
defparam rpll_inst.DEVICE = "GW2AR-18";

endmodule //Gowin_rPLL
59 changes: 59 additions & 0 deletions src/gowin_rpll_usb.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

module Gowin_rPLL_usb (clkout, clkoutp, lock, reset, clkin);

output clkout;
output clkoutp;
output lock;
input reset;
input clkin;

wire clkoutd_o;
wire clkoutd3_o;
wire gw_gnd;

assign gw_gnd = 1'b0;

rPLL rpll_inst (
.CLKOUT(clkout),
.LOCK(lock),
.CLKOUTP(clkoutp),
.CLKOUTD(clkoutd_o),
.CLKOUTD3(clkoutd3_o),
.RESET(reset),
.RESET_P(gw_gnd),
.CLKIN(clkin),
.CLKFB(gw_gnd),
.FBDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
.IDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
.ODSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
.PSDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
.DUTYDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
.FDLY({gw_gnd,gw_gnd,gw_gnd,gw_gnd})
);

// 27 -> 12 Mhz low-speed USB clock
defparam rpll_inst.FCLKIN = "27";
defparam rpll_inst.IDIV_SEL = 8;
defparam rpll_inst.FBDIV_SEL = 3;
defparam rpll_inst.ODIV_SEL = 64;

defparam rpll_inst.DYN_IDIV_SEL = "false";
defparam rpll_inst.DYN_FBDIV_SEL = "false";
defparam rpll_inst.DYN_ODIV_SEL = "false";
defparam rpll_inst.PSDA_SEL = "1000";
defparam rpll_inst.DYN_DA_EN = "false";
defparam rpll_inst.DUTYDA_SEL = "1000";
defparam rpll_inst.CLKOUT_FT_DIR = 1'b1;
defparam rpll_inst.CLKOUTP_FT_DIR = 1'b1;
defparam rpll_inst.CLKOUT_DLY_STEP = 0;
defparam rpll_inst.CLKOUTP_DLY_STEP = 0;
defparam rpll_inst.CLKFB_SEL = "internal";
defparam rpll_inst.CLKOUT_BYPASS = "false";
defparam rpll_inst.CLKOUTP_BYPASS = "false";
defparam rpll_inst.CLKOUTD_BYPASS = "false";
defparam rpll_inst.DYN_SDIV_SEL = 2;
defparam rpll_inst.CLKOUTD_SRC = "CLKOUT";
defparam rpll_inst.CLKOUTD3_SRC = "CLKOUT";
defparam rpll_inst.DEVICE = "GW2AR-18";

endmodule //Gowin_rPLL
130 changes: 93 additions & 37 deletions src/nes_tang20k.v
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ module NES_Tang20k(
input joystick_miso2,
output reg joystick_cs2,

// USB
inout usbdm,
inout usbdp,
inout usbdm2,
inout usbdp2,
// output clk_usb,

// HDMI TX
output tmds_clk_n,
output tmds_clk_p,
Expand All @@ -64,12 +71,21 @@ always @(posedge clk) begin
end

`ifndef VERILATOR
// NES PPU clock 5.369 * 7 = 37.6
Gowin_rPLL_nes pll_nes(
.clkin(sys_clk),
.clkout(clk), // FREQ main clock
.clkoutp(clk_sdram) // FREQ main clock phase shifted
);
// Gowin_rPLL_nes pll_nes(
// .clkin(sys_clk),
// .clkout(clk), // FREQ main clock
// .clkoutp(clk_sdram) // FREQ main clock phase shifted
// );
wire clk = sys_clk;
wire clk_sdram = ~sys_clk;
wire clk_usb;

// USB clock 12Mhz
Gowin_rPLL_usb pll_nes(
.clkin(sys_clk),
.clkout(clk_usb), // 12Mhz usb clock
.clkoutp()
);

// HDMI domain clocks
wire clk_p; // 720p pixel clock: 74.25 Mhz
Expand Down Expand Up @@ -161,11 +177,16 @@ UartDemux #(.FREQ(FREQ), .BAUDRATE(BAUDRATE)) uart_demux(
O is A, X is B
*/
wire [7:0] joy_rx[0:1], joy_rx2[0:1]; // 6 RX bytes for all button/axis state
wire [7:0] usb_btn, usb_btn2;
wire usb_btn_x, usb_btn_y, usb_btn_x2, usb_btn_y2;
wire usb_conerr, usb_conerr2;
wire auto_square, auto_triangle, auto_square2, auto_triangle2;
wire [7:0] nes_btn = {~joy_rx[0][5], ~joy_rx[0][7], ~joy_rx[0][6], ~joy_rx[0][4],
~joy_rx[0][3], ~joy_rx[0][0], ~joy_rx[1][6] | auto_square, ~joy_rx[1][5] | auto_triangle};
~joy_rx[0][3], ~joy_rx[0][0], ~joy_rx[1][6] | auto_square, ~joy_rx[1][5] | auto_triangle} |
usb_btn;
wire [7:0] nes_btn2 = {~joy_rx2[0][5], ~joy_rx2[0][7], ~joy_rx2[0][6], ~joy_rx2[0][4],
~joy_rx2[0][3], ~joy_rx2[0][0], ~joy_rx2[1][6] | auto_square2, ~joy_rx2[1][5] | auto_triangle2};
~joy_rx2[0][3], ~joy_rx2[0][0], ~joy_rx2[1][6] | auto_square2, ~joy_rx2[1][5] | auto_triangle2} |
usb_btn2;

// Joypad handling
always @(posedge clk) begin
Expand Down Expand Up @@ -349,18 +370,32 @@ dualshock_controller controller2 (
.I_VIB_SW(2'b00), .I_VIB_DAT(8'hff) // no vibration
);

Autofire af_square (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx[1][7]), .out(auto_square));
Autofire af_triangle (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx[1][4]), .out(auto_triangle));
Autofire af_square2 (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx2[1][7]), .out(auto_square2));
Autofire af_triangle2 (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx2[1][4]), .out(auto_triangle2));
Autofire af_square (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx[1][7] | usb_btn_y), .out(auto_square)); // B
Autofire af_triangle (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx[1][4] | usb_btn_x), .out(auto_triangle)); // A
Autofire af_square2 (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx2[1][7] | usb_btn_y2), .out(auto_square2));
Autofire af_triangle2 (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx2[1][4] | usb_btn_x2), .out(auto_triangle2));

wire [63:0] dbg_hid_report;
wire [3:0] dbg_dev;
wire [15:0] dbg_vid, dbg_pid;
usb_gamepad usb_controller (
.usbclk(clk_usb), .usbrst_n(sys_resetn),
.usb_dm(usbdm), .usb_dp(usbdp), .btn_nes(usb_btn), .btn_x(usb_btn_x), .btn_y(usb_btn_y), .conerr(usb_conerr),
.dbg_hid_report(), .dbg_dev(), .dbg_vid(), .dbg_pid()
);
usb_gamepad usb_controller2 (
.usbclk(clk_usb), .usbrst_n(sys_resetn),
.usb_dm(usbdm2), .usb_dp(usbdp2), .btn_nes(usb_btn2), .btn_x(usb_btn_x2), .btn_y(usb_btn_y2), .conerr(usb_conerr2),
.dbg_hid_report(dbg_hid_report), .dbg_dev(dbg_dev), .dbg_vid(dbg_vid), .dbg_pid(dbg_pid)
);

//
// Print control
//
`include "print.v"
defparam tx.uart_freq=BAUDRATE;
defparam tx.clk_freq=FREQ;
assign print_clk = clk;
assign print_clk = sys_clk;
assign UART_TXD = uart_txp;

reg[3:0] state_0;
Expand All @@ -378,23 +413,15 @@ reg [15:0] indata_clk_count = 0;

reg [3:0] sd_state0 = 0;

reg [19:0] timer; // 27 times per second
reg [19:0] timer; // 37 times per second
always @(posedge clk) timer <= timer + 1;

// `define HID_REPORT

always@(posedge clk)begin
state_0<={2'b0, loader_done};
state_1<=state_0;

/*
if (timer == 0) begin
`print({joy_rx[0], joy_rx[1], joy_rx2[0], joy_rx2[1], nes_btn, nes_btn2}, 6);
// `print({3'b0, sd_active, 3'b0, sd_total, sd_rsector, sd_last_sector}, 8);
end
if (timer == 20'b1000_0000_0000_0000_0000) begin
`print("\n", STR);
end
*/

if (uart_demux.write)
recv_packets <= recv_packets + 1;

Expand All @@ -407,18 +434,46 @@ always@(posedge clk)begin
end
end

// if (sd_state != sd_state0) begin
// if (sd_state == SD_READ_META) begin
// `print("Reading SDcard\n", STR);
// end
// if (sd_state == SD_START_SECTOR) begin
// if (sd_rsector[15:0] == 16'b0) begin
// `print(sd_romlen, 3);
// end else
// `print(sd_rsector[15:0], 2);
// end
// sd_state0 <= sd_state;
// end
`ifdef HID_REPORT
if (timer == 20'h00000)
`print("hid=", STR);
if (timer == 20'h10000)
`print(dbg_hid_report, 8);
if (timer == 20'h20000)
`print(", vidpid=", STR);
if (timer == 20'h30000)
`print({dbg_vid, dbg_pid}, 4);
if (timer == 20'h40000)
`print(", dev=", STR);
if (timer == 20'h50000)
`print({4'b0, dbg_dev}, 1);
if (timer == 20'h60000)
`print(", ds2[2]=", STR);
if (timer == 20'h70000)
`print({joy_rx[0], joy_rx[1], joy_rx2[0], joy_rx2[1]}, 4);
if (timer == 20'h80000)
`print(", usb_btn[2]=", STR);
if (timer == 20'h90000)
`print({usb_btn, usb_btn2}, 2);

if (timer == 20'hf0000)
`print("\n", STR);
`endif

`ifdef PRINT_SD
if (sd_state != sd_state0) begin
if (sd_state == SD_READ_META) begin
`print("Reading SDcard\n", STR);
end
if (sd_state == SD_START_SECTOR) begin
if (sd_rsector[15:0] == 16'b0) begin
`print(sd_romlen, 3);
end else
`print(sd_rsector[15:0], 2);
end
sd_state0 <= sd_state;
end
`endif

`ifdef COLOR_TRACING
// print some color values
Expand Down Expand Up @@ -490,6 +545,7 @@ end

`endif

assign led = ~{~UART_RXD, loader_done};
assign led = ~{~UART_RXD, usb_conerr, loader_done};
// assign led = ~usb_btn;

endmodule
1 change: 1 addition & 0 deletions src/nes_tang20k.vh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ localparam FREQ=27_000_000; // at least 10x baudrate
// localparam FREQ=37_800_000;

// UART baudrate: BAUDRATE <= FREQ/10
// localparam BAUDRATE=115200;
localparam BAUDRATE=921600;

// define this to execute one NES cycle per 0.01 second and print the operation done
Expand Down
21 changes: 21 additions & 0 deletions src/nestang.cst
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,30 @@ IO_PORT "UART_TXD" IO_TYPE=LVCMOS33;
//IO_LOC "UART_RXD" 31;
//IO_PORT "UART_RXD" IO_TYPE=LVCMOS33 PULL_MODE=NONE;

// USB ports
IO_LOC "usbdp" 42;
IO_PORT "usbdp" PULL_MODE=DOWN IO_TYPE=LVCMOS33;
IO_LOC "usbdm" 41;
IO_PORT "usbdm" PULL_MODE=DOWN IO_TYPE=LVCMOS33;
IO_LOC "usbdp2" 56;
IO_PORT "usbdp2" PULL_MODE=DOWN IO_TYPE=LVCMOS33;
IO_LOC "usbdm2" 54;
IO_PORT "usbdm2" PULL_MODE=DOWN IO_TYPE=LVCMOS33;

// USB debug board
//IO_LOC "usbdp" 41; // LCD_R4
//IO_PORT "usbdp" PULL_MODE=DOWN IO_TYPE=LVCMOS33;
//IO_LOC "usbdm" 42; // LCD_R3
//IO_PORT "usbdm" PULL_MODE=DOWN IO_TYPE=LVCMOS33;
//IO_LOC "clk_usb" 56; // for logic analyzer
//IO_PORT "clk_usb" PULL_MODE=NONE IO_TYPE=LVCMOS33;


// 2 LEDs for debug
IO_LOC "led[1]" 16;
IO_PORT "led[1]" IO_TYPE=LVCMOS33 PULL_MODE=UP DRIVE=8;
IO_LOC "led[0]" 15;
IO_PORT "led[0]" IO_TYPE=LVCMOS33 PULL_MODE=UP DRIVE=8;


// pinout: https://wiki.sipeed.com/hardware/zh/tang/tang-nano-20k/assets/nano_20k/tang_nano_20k_pinlabel.png
5 changes: 4 additions & 1 deletion src/nestang.sdc
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@

// NES clocks
create_clock -name clk -period 37.04 [get_nets {clk}] // 27 Mhz
create_clock -name clk -period 37.04 [get_nets {sys_clk}] // 27 Mhz
//create_generated_clock -name clk -source [get_nets {pclk}] -master_clock pclk -divide_by 3 [get_nets {clk}] // 32.25 Mhz

// USB clock
create_clock -name clk_usb -period 83.33 [get_nets {clk_usb}] // 12 Mhz

// HDMI clocks
create_clock -name clk_p5 -period 2.6936 [get_nets {clk_p5}] // 371.25 Mhz
//create_generated_clock -name clk_p -source [get_nets {clk_p}] -master_clock clk_p5 -divide_by 5 [get_nets {clk_p}] // 74.25 Mhz: 720p pixel clock
Expand Down
Loading

0 comments on commit adcb3cc

Please sign in to comment.