diff --git a/doc/images/usb_gamepad1.jpg b/doc/images/usb_gamepad1.jpg
new file mode 100644
index 0000000..24b4f50
Binary files /dev/null and b/doc/images/usb_gamepad1.jpg differ
diff --git a/doc/images/usb_gamepad2.jpg b/doc/images/usb_gamepad2.jpg
new file mode 100644
index 0000000..1a72eb9
Binary files /dev/null and b/doc/images/usb_gamepad2.jpg differ
diff --git a/doc/usb_gamepad.md b/doc/usb_gamepad.md
new file mode 100644
index 0000000..3017b0c
--- /dev/null
+++ b/doc/usb_gamepad.md
@@ -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.
+
+
+
+
+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.
+
diff --git a/nes.gprj b/nes.gprj
index a59af81..8e30d24 100644
--- a/nes.gprj
+++ b/nes.gprj
@@ -16,6 +16,7 @@
+
@@ -39,6 +40,8 @@
+
+
diff --git a/src/gowin_rpll_hdmi/gowin_rpll.v b/src/gowin_rpll_hdmi/gowin_rpll.v
index 6fd6974..a59a551 100644
--- a/src/gowin_rpll_hdmi/gowin_rpll.v
+++ b/src/gowin_rpll_hdmi/gowin_rpll.v
@@ -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
diff --git a/src/gowin_rpll_nes/gowin_rpll.v b/src/gowin_rpll_nes/gowin_rpll.v
index 2bbd4dc..e988acc 100644
--- a/src/gowin_rpll_nes/gowin_rpll.v
+++ b/src/gowin_rpll_nes/gowin_rpll.v
@@ -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
diff --git a/src/gowin_rpll_usb.v b/src/gowin_rpll_usb.v
new file mode 100644
index 0000000..6026de4
--- /dev/null
+++ b/src/gowin_rpll_usb.v
@@ -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
diff --git a/src/nes_tang20k.v b/src/nes_tang20k.v
index a1561ac..769b29c 100644
--- a/src/nes_tang20k.v
+++ b/src/nes_tang20k.v
@@ -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,
@@ -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
@@ -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
@@ -349,10 +370,24 @@ 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
@@ -360,7 +395,7 @@ Autofire af_triangle2 (.clk(clk), .resetn(sys_resetn), .btn(~joy_rx2[1][4]), .ou
`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;
@@ -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;
@@ -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
@@ -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
\ No newline at end of file
diff --git a/src/nes_tang20k.vh b/src/nes_tang20k.vh
index cf09e38..8bbf21a 100644
--- a/src/nes_tang20k.vh
+++ b/src/nes_tang20k.vh
@@ -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
diff --git a/src/nestang.cst b/src/nestang.cst
index 6cfe774..a324986 100644
--- a/src/nestang.cst
+++ b/src/nestang.cst
@@ -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
\ No newline at end of file
diff --git a/src/nestang.sdc b/src/nestang.sdc
index fea0613..34f5cbf 100644
--- a/src/nestang.sdc
+++ b/src/nestang.sdc
@@ -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
diff --git a/src/usb_gamepad.v b/src/usb_gamepad.v
new file mode 100644
index 0000000..2ff9c95
--- /dev/null
+++ b/src/usb_gamepad.v
@@ -0,0 +1,325 @@
+// Original author: hi631@github
+// https://github.com/hi631/tang-nano-9K
+
+module usb_gamepad (
+ input usbclk, // 12MHz
+ input usbrst_n, // reset
+ inout usb_dm, usb_dp,
+ output reg [7:0] btn_nes,
+ output reg btn_x, btn_y, // for auto-fire
+ output reg [63:0] dbg_hid_report, // last HID report
+ output reg [15:0] dbg_vid,
+ output reg [15:0] dbg_pid,
+ output [3:0] dbg_dev,
+ output conerr
+);
+
+wire dtrdy, dtstb; // data ready and strobe
+wire [7:0] ukpdat; // actual data
+wire vidpid; // last vid/pid was valid
+//wire conerr; // connection error
+ukp ukp(
+ .usbrst_n(usbrst_n), .usbclk(usbclk),
+ .usb_dp(usb_dp), .usb_dm(usb_dm), .usb_oe(),
+ .ukprdy(dtrdy), .ukpstb(dtstb), .ukpdat(ukpdat), .vidpid(vidpid),
+ .conerr(conerr) );
+
+reg [3:0] rcvct; // counter for recv data
+reg dtstbd, dtrdyd; // delayed dtstb and dtrdy
+reg [15:0] tmp_vid, tmp_pid; // temporary VID and PID
+
+// Device types, see vidpid_recognition below
+localparam D_GENERIC = 0;
+localparam D_GAMEPAD = 1;
+localparam D_DS2_ADAPTER = 2;
+
+reg [3:0] dev = D_GENERIC; // device type recognized through VID/PID
+assign dbg_dev = dev;
+reg valid = 0; // whether current scancode is valid
+
+reg btn_a, btn_b, btn_sel, btn_sta;
+reg btn_al, btn_ar, btn_ad, btn_au; // left, right, down, up
+
+always @(posedge usbclk) begin : process_in_data
+ dtrdyd <= dtrdy; dtstbd <= dtstb;
+ if(~dtrdy) rcvct <= 0;
+ else begin
+ if(dtstb && ~dtstbd) begin
+ case(rcvct)
+ 0: begin
+ tmp_vid[7:0] <= ukpdat; // collect VID/PID from the device descriptor
+ dbg_hid_report[7:0] <= ukpdat;
+ end
+ 1: begin
+ tmp_vid[15:8] <= ukpdat;
+ dbg_hid_report[15:8] <= ukpdat;
+ end
+ 2: begin
+ tmp_pid[7:0] <= ukpdat;
+ dbg_hid_report[23:16] <= ukpdat;
+ end
+ 3: begin
+ tmp_pid[15:8] <= ukpdat;
+ dbg_hid_report[31:24] <= ukpdat;
+ end
+ 4: dbg_hid_report[39:32] <= ukpdat;
+ 5: dbg_hid_report[47:40] <= ukpdat;
+ 6: dbg_hid_report[55:48] <= ukpdat;
+ 7: dbg_hid_report[63:56] <= ukpdat;
+ endcase
+ // Generic gamepad handling.
+ // A typical scheme:
+ // - d[3] is X axis (0: left, 255: right)
+ // - d[4] is Y axis
+ // - d[5][7:4] is buttons YBAX
+ // - d[6][5:4] is buttons START,SELECT
+ // Variations:
+ // - Some gamepads uses d[0] and d[1] for X and Y axis.
+ // - Some transmits a different set when d[0][1:0] is 2 (a dualshock adapater)
+ case (rcvct)
+ 0: begin
+ if (ukpdat[1:0] != 2'b10) begin
+ // for DualShock2 adapter, 2'b10 marks an irrelevant record
+ valid <= 1;
+ btn_al <= 0; btn_ar <= 0; btn_au <= 0; btn_ad <= 0;
+ end else
+ valid <= 0;
+ if (ukpdat==8'h00) {btn_al, btn_ar} <= 2'b10;
+ if (ukpdat==8'hff) {btn_al, btn_ar} <= 2'b01;
+ end
+ 1: begin
+ if (ukpdat==8'h00) {btn_au, btn_ad} <= 2'b10;
+ if (ukpdat==8'hff) {btn_au, btn_ad} <= 2'b01;
+ end
+ 3: if (valid) begin
+ if (ukpdat[7:6]==2'b00) {btn_al, btn_ar} <= 2'b10;
+ if (ukpdat[7:6]==2'b11) {btn_al, btn_ar} <= 2'b01;
+ end
+ 4: if (valid) begin
+ if (ukpdat[7:6]==2'b00) {btn_au, btn_ad} <= 2'b10;
+ if (ukpdat[7:6]==2'b11) {btn_au, btn_ad} <= 2'b01;
+ end
+ 5: if (valid) begin
+ btn_x <= ukpdat[4];
+ btn_a <= ukpdat[5];
+ btn_b <= ukpdat[6];
+ btn_y <= ukpdat[7];
+ end
+ 6: if (valid) begin
+ btn_sel <= ukpdat[4];
+ btn_sta <= ukpdat[5];
+ end
+ endcase
+ // TODO: add any special handling if needed
+ // (using the detected controller type in 'dev')
+
+ rcvct <= rcvct + 1;
+ end
+ end
+ if(~dtrdy && dtrdyd) btn_nes <= {btn_ar,btn_al,btn_ad,btn_au,btn_sta,btn_sel,btn_b,btn_a};
+end
+
+always @(posedge usbclk) begin : vidpid_recognition
+ if (vidpid) begin
+ dbg_vid <= tmp_vid;
+ dbg_pid <= tmp_pid;
+ case({tmp_vid, tmp_pid})
+ 32'h081F_E401: // "Gamepad" - snes-style gamepad
+ dev <= D_GAMEPAD;
+ 32'h0810_0001: // "Twin USB Joystick" - DS2 USB adapter
+ dev <= D_DS2_ADAPTER;
+ default:
+ dev <= D_GENERIC;
+ endcase
+ end
+end
+
+endmodule
+
+module ukp(
+ input usbrst_n,
+ input usbclk, // 12MHz clock
+ inout usb_dp, usb_dm, // D+, D-
+ output usb_oe,
+ output reg ukprdy, // data frame is outputing
+ output ukpstb, // strobe for a byte within the frame
+ output reg [7:0] ukpdat, // output data when ukpstb=1
+ output reg vidpid, // VID/PID in last response is valid
+ output conerr
+);
+
+ parameter S_OPCODE = 0;
+ parameter S_LDI0 = 1;
+ parameter S_LDI1 = 2;
+ parameter S_B0 = 3;
+ parameter S_B1 = 4;
+ parameter S_B2 = 5;
+ parameter S_S0 = 6;
+ parameter S_S1 = 7;
+ parameter S_S2 = 8;
+ parameter S_TOGGLE0 = 9;
+
+ wire [3:0] inst;
+ reg [3:0] insth;
+ wire sample; // 1: an IN sample is available
+ reg connected = 0, inst_ready = 0, up = 0, um = 0, cond = 0, nak = 0, dmis = 0;
+ reg ug, ugw, nrzon; // ug=1: output enabled, 0: hi-Z
+ reg bank = 0, record1 = 0;
+ reg [1:0] mbit = 0; // 1: out4/outb is transmitting
+ reg [3:0] state = 0, stated;
+ reg [7:0] wk = 0; // W register
+ reg [7:0] sb = 0; // out value
+ reg [3:0] sadr; // out4/outb write ptr
+ reg [13:0] pc = 0, wpc; // program counter, wpc = next pc
+ reg [2:0] timing = 0; // T register (0~7)
+ reg [3:0] lb4 = 0, lb4w;
+ reg [13:0] interval = 0;
+ reg [6:0] bitadr = 0; // 0~127
+ reg [7:0] data = 0; // received data
+ reg [2:0] nrztxct, nrzrxct; // NRZI trans/recv count for bit stuffing
+ wire interval_cy = interval == 12001;
+ wire next = ~(state == S_OPCODE & (
+ inst ==2 & dmi | // start
+ (inst==4 || inst==5) & timing != 0 | // out0/hiz
+ inst ==13 & (~sample | (dpi | dmi) & wk != 1) | // in
+ inst ==14 & ~interval_cy // wait
+ ));
+ wire branch = state == S_B1 & cond;
+ wire retpc = state == S_OPCODE && inst==7 ? 1 : 0;
+ wire jmppc = state == S_OPCODE && inst==15 ? 1 : 0;
+ wire dbit = sb[7-sadr[2:0]];
+ wire record;
+ reg dmid;
+ reg [23:0] conct;
+ wire conerr = conct[23] || ~usbrst_n;;
+
+ usb_gamepad_rom ukprom(.clk(usbclk), .adr(pc), .data(inst));
+
+ always @(posedge usbclk) begin
+ if(~usbrst_n) begin
+ pc <= 0; connected <= 0; cond <= 0; inst_ready <= 0; state <= S_OPCODE; timing <= 0;
+ mbit <= 0; bitadr <= 0; nak <= 1; ug <= 0;
+ end else begin
+ dpi <= usb_dp; dmi <= usb_dm;
+ vidpid <= 0; // ensure pulse
+ if (inst_ready) begin
+ // Instruction decoding
+ case(state)
+ S_OPCODE: begin
+ insth <= inst;
+ if(inst==1) state <= S_LDI0; // op=ldi
+ if(inst==3) begin sadr <= 3; state <= S_S0; end // op=out4
+ if(inst==4) begin ug <= 9; up <= 0; um <= 0; end
+ if(inst==5) begin ug <= 0; end
+ if(inst==6) begin sadr <= 7; state <= S_S0; end // op=outb
+ if (inst[3:2]==2'b10) begin // op=10xx(BZ,BC,BNAK,DJNZ)
+ state <= S_B0;
+ case (inst[1:0])
+ 2'b00: cond <= ~dmi;
+ 2'b01: cond <= connected;
+ 2'b10: cond <= nak;
+ 2'b11: cond <= wk != 1;
+ endcase
+ end
+ if(inst==11 | inst==13 & sample) wk <= wk - 8'd1; // op=DJNZ,IN
+ if(inst==15) begin state <= S_B2; cond <= 1; end // op=jmp
+ if(inst==12) state <= S_TOGGLE0;
+ end
+ // Instructions with operands
+ // ldi
+ S_LDI0: begin wk[3:0] <= inst; state <= S_LDI1; end
+ S_LDI1: begin wk[7:4] <= inst; state <= S_OPCODE; end
+ // branch/jmp
+ S_B2: begin lb4w <= inst; state <= S_B0; end
+ S_B0: begin lb4 <= inst; state <= S_B1; end
+ S_B1: state <= S_OPCODE;
+ // out
+ S_S0: begin sb[3:0] <= inst; state <= S_S1; end
+ S_S1: begin sb[7:4] <= inst; state <= S_S2; mbit <= 1; end
+ // toggle and vidpid
+ S_TOGGLE0: begin
+ if (inst == 1) connected <= ~connected; // toggle
+ else vidpid <= 1; // vidpid
+ state <= S_OPCODE;
+ end
+ endcase
+ // pc control
+ if (mbit==0) begin
+ if(jmppc) wpc <= pc + 4;
+ if (next | branch | retpc) begin
+ if(retpc) pc <= wpc; // ret
+ else if(branch)
+ if(insth==15) // jmp
+ pc <= { inst, lb4, lb4w, 2'b00 };
+ else // branch
+ pc <= { 4'b0000, inst, lb4, 2'b00 };
+ else pc <= pc + 1; // next
+ inst_ready <= 0;
+ end
+ end
+ end
+ else inst_ready <= 1;
+ // bit transmission (out4/outb)
+ if (mbit==1 && timing == 0) begin
+ if(ug==0) nrztxct <= 0;
+ else
+ if(dbit) nrztxct <= nrztxct + 1;
+ else nrztxct <= 0;
+ if(insth == 4'd6) begin
+ if(nrztxct!=6) begin up <= dbit ? up : ~up; um <= dbit ? ~up : up; end
+ else begin up <= ~up; um <= up; nrztxct <= 0; end
+ end else begin
+ up <= sb[{1'b1,sadr[1:0]}]; um <= sb[sadr[2:0]];
+ end
+ ug <= 1'b1;
+ if(nrztxct!=6) sadr <= sadr - 4'd1;
+ if(sadr==0) begin mbit <= 0; state <= S_OPCODE; end
+ end
+ // start instruction
+ dmid <= dmi;
+ if (inst_ready & state == S_OPCODE & inst == 4'b0010) begin // op=start
+ bitadr <= 0; nak <= 1; nrzrxct <= 0;
+ end else
+ if(ug==0 && dmi!=dmid) timing <= 1;
+ else timing <= timing + 1;
+ // IN instruction
+ if (sample) begin
+ if (bitadr == 8) nak <= dmi;
+ if(nrzrxct!=6) begin
+ data[6:0] <= data[7:1];
+ data[7] <= dmis ~^ dmi; // ~^/^~ is XNOR, basically testing bit equality
+ bitadr <= bitadr + 1; nrzon <= 0;
+ end else nrzon <= 1;
+ dmis <= dmi;
+ if(dmis ~^ dmi) nrzrxct <= nrzrxct + 1;
+ else nrzrxct <= 0;
+ end
+ if(ug==0) begin
+ if(bitadr==24) ukprdy <= 1; // ignore first 3 bytes
+ if(bitadr==88) ukprdy <= 0; // output next 8 bytes
+ end
+ if((bitadr>11 & bitadr[2:0] == 3'b000) & (timing == 2)) ukpdat <= data;
+ // Timing
+ interval <= interval_cy ? 0 : interval + 1;
+ record1 <= record;
+ if (~record & record1) bank <= ~bank;
+ // Connection status & WDT
+ ukprdyd <= ukprdy;
+ if(ukprdy && ~ukprdyd) conct <= 0;
+ else begin
+ if(conct[23:22]!=2'b11) conct <= conct + 1;
+ else begin pc <= 0; conct <= 0; end // !! WDT ON
+ end
+ end
+ end
+
+ assign usb_dp = ug ? up : 1'bZ;
+ assign usb_dm = ug ? um : 1'bZ;
+ assign usb_oe = ug;
+ assign sample = inst_ready & state == S_OPCODE & inst == 4'b1101 & timing == 4; // IN
+ assign record = connected & ~nak;
+ assign ukpstb = ~nrzon & ukprdy & (bitadr[2:0] == 3'b100) & (timing == 2);
+ reg dpi, dmi;
+ reg ukprdyd;
+endmodule
+
diff --git a/src/usb_gamepad/asukp b/src/usb_gamepad/asukp
new file mode 100644
index 0000000..737755b
--- /dev/null
+++ b/src/usb_gamepad/asukp
@@ -0,0 +1,104 @@
+#! /usr/bin/perl
+# UKP assembler
+
+%inst = ("nop" , 0, "ldi" , 1, "start", 2, "out4", 3,
+ "out0" , 4, "hiz" , 5, "outb" , 6, "ret" , 7,
+ "bz" , 8, "bc" , 9, "bnak" , 10, "djnz", 11,
+ "toggle", 12, "vidpid", 12, "in" , 13, "wait" , 14, "jmp", 15 );
+
+open(SRC, "ukp.s") || die;
+while (&getline) { # calculate all label addresses
+ if (/^(\w+):/) {
+ if (defined($label{$1})) {
+ printf STDERR "$_ already defined\n";
+ exit 1;
+ }
+ $pc = $pc + 3 & ~3;
+ $label{$1} = $pc;
+ printf "pc=%03x\t%s\n", $pc, $1;
+ }
+ else {
+ @_ = split;
+ unless (defined($inst{$_[0]})) {
+ printf STDERR "syntax error: $_\n";
+ exit 1;
+ }
+ $code = $inst{$_[0]};
+ if($code==15) {
+ $pc += 4;
+ } else {
+ $pc += $code == 1 || $code >= 8 && $code < 12 || $code == 3 || $code == 6 ? 3 :
+ $code == 12 ? 2 : 1;
+ }
+ }
+}
+seek(SRC, 0, SEEK_END);
+$pc = 0;
+open(DST, "> usb_gamepad_rom.v") || die;
+select DST;
+print <> 4);
+ }
+
+ elsif ($code >= 8 && $code < 12 || $code == 15) { # jump
+ unless (defined($label{$_[1]})) {
+ printf STDERR "$_[1] not defined\n";
+ exit 1;
+ }
+ $adr = $label{$_[1]} >> 2;
+ putline($adr & 15);
+ putline(($adr >> 4) & 15);
+ if($code == 15) {
+ putline(($adr >> 8) & 15);
+ }
+ }
+ }
+}
+close SRC;
+print "\t\t\tdefault: data = 4'hX;\n\t\tendcase\n\tend\nendmodule\n";
+close DST;
+system("mv ./usb_gamepad_rom.v ../usb_gamepad_rom.v");
+exit 0;
+
+sub getline {
+ do {
+ return 0 unless $_ = ;
+ chomp;
+ s/\s*;.*$//;
+ } while (/^\s*$/);
+ 1;
+}
+
+sub putline {
+ printf "\t\t\t10'h%03x: data = 4'h%x;%s\n", $pc++, shift(@_), $scd;
+}
diff --git a/src/usb_gamepad/ukp.s b/src/usb_gamepad/ukp.s
new file mode 100644
index 0000000..cfc8588
--- /dev/null
+++ b/src/usb_gamepad/ukp.s
@@ -0,0 +1,256 @@
+; USB gamepad firmware for UKP
+; By hi631, nand2mario
+
+cstart:
+; ---- interrupt transfer interval (10-1mS)
+ ldi 9
+cstart2:
+ wait
+ bc connected
+ bz cstart
+
+; ---- wait 200mS after device attached
+ ldi 200
+w200ms:
+ wait
+ djnz w200ms
+
+; ---- USB bus reset
+ out0
+ ldi 10
+busrstlp:
+ wait
+ djnz busrstlp
+ hiz
+
+; ---- 40mS wait
+ ldi 40
+w40ms:
+ wait
+ out4 0x03
+ hiz
+ djnz w40ms
+ wait
+
+; nand2mario: see USB device initialization sequence
+; https://www.usbmadesimple.co.uk/ums_4.htm
+; CRC calculator: https://www.lddgo.net/en/encrypt/crc
+
+; ---- send set address 1
+ jmp setadr1
+ hiz
+
+; ---- recieve
+ jmp rcvdt
+
+; ---- send IN(0,0)
+sendinlp:
+ jmp in00
+ hiz
+
+; ---- receive
+ jmp rcvdt
+ bnak sendinlp
+
+; ---- send ACK
+ jmp sendack
+ hiz
+
+; ---- wait 1mS
+ wait
+
+; nand2mario: request device descriptor and receive ACK
+ jmp getdesc
+ hiz
+ jmp rcvdt
+
+; ---- send IN(1,0) and receive 1st half of device descriptor
+wait_desc:
+ jmp in10
+ hiz
+ jmp rcvdt
+ bnak wait_desc
+; send ACK
+ jmp sendack
+ hiz
+
+; request 2nd half of device descriptor
+wait_desc2:
+ jmp in10
+ hiz
+ jmp rcvdt
+ bnak wait_desc2
+ vidpid ; register VID and PID just received
+; send ACK
+ jmp sendack
+ hiz
+
+; TODO: need to send a status transaction
+; https://www.beyondlogic.org/usbnutshell/usb4.shtml#Control
+
+; ---- wait 1mS
+ wait
+
+; ---- send set configuration 1
+ jmp setconfig1
+ hiz
+
+; ---- recieve
+ jmp rcvdt
+
+; ---- send IN(1,0)
+in10lp:
+ jmp in10
+ hiz
+
+; ---- recieve
+ jmp rcvdt
+ bnak in10lp
+
+; ---- send ACK
+ jmp sendack
+ hiz
+ toggle
+ jmp cstart
+
+; -------------------
+; when connected
+; -------------------
+connected:
+ bz connerr
+
+ out4 0x03
+ hiz
+ djnz cstart2
+ wait
+
+; ---- IN(1,1) (interrupt transfer)
+ jmp in11
+ hiz
+
+; ---- recieve
+ jmp rcvdt
+ bnak cstart
+
+; ---- send ACK
+ jmp sendack
+ hiz
+
+; ---- jump startf
+ jmp cstart
+
+; ---- jupm start(&toggle)
+connerr:
+ toggle
+ jmp cstart
+
+; --------------
+; sub
+; --------------
+setadr1:
+ outb 0x80
+ outb 0x2d
+ outb 0x00
+ outb 0x10
+ out4 0x03
+
+ outb 0x80
+ outb 0xc3
+ outb 0x00
+ outb 0x05
+ outb 0x01
+ outb 0x00
+ outb 0x00
+ outb 0x00
+ outb 0x00
+ outb 0x00
+ outb 0xeb
+ outb 0x25
+ out4 0x03
+ ret
+
+getdesc: ; get device descriptor
+ outb 0x80 ; SYNC
+ outb 0x2d ; PID
+ outb 0x01 ; ADDR + ENDP
+ outb 0xe8 ; + CRC5
+ out4 0x03 ; EOP
+
+ outb 0x80 ; SYNC
+ outb 0xc3 ; PID=DATA0
+ outb 0x80 ; bmRequestType: 0
+ outb 0x06 ; bRequest=1 Get_Descriptor
+ outb 0x00 ; Desc Index: 0
+ outb 0x01 ; Desc Type: 1 Device
+ outb 0x00 ; Language ID: 0
+ outb 0x00 ;
+ outb 0x12 ; wLength = 18
+ outb 0x00
+ outb 0xE0 ; CRC16
+ outb 0xF4
+ out4 0x03 ; EOP
+ ret
+
+setconfig1:
+ outb 0x80
+ outb 0x2d
+ outb 0x01
+ outb 0xe8
+ out4 0x03
+
+ outb 0x80
+ outb 0xc3
+ outb 0x00
+ outb 0x09
+ outb 0x01
+ outb 0x00
+ outb 0x00
+ outb 0x00
+ outb 0x00
+ outb 0x00
+ outb 0x27
+ outb 0x25
+ out4 0x03
+ ret
+
+rcvdt:
+ ldi 104
+ start
+ in
+rcvdt2:
+ ldi 2
+rcvdt3:
+ bz rcvdt2
+ djnz rcvdt3
+ ret
+
+in00:
+ outb 0x80
+ outb 0x69
+ outb 0x00
+ outb 0x10
+ out4 0x03
+ ret
+
+in10:
+ outb 0x80
+ outb 0x69
+ outb 0x01
+ outb 0xe8
+ out4 0x03
+ ret
+
+in11:
+ outb 0x80
+ outb 0x69
+ outb 0x81
+ outb 0x58
+ out4 0x03
+ ret
+
+sendack:
+ outb 0x80
+ outb 0xd2
+ out4 0x03
+ ret
+prgend:
diff --git a/src/usb_gamepad_rom.v b/src/usb_gamepad_rom.v
new file mode 100644
index 0000000..e0378ab
--- /dev/null
+++ b/src/usb_gamepad_rom.v
@@ -0,0 +1,477 @@
+module usb_gamepad_rom(clk, adr, data);
+ input clk;
+ input [13:0] adr;
+ output [3:0] data;
+ reg [3:0] data;
+ always @(posedge clk) begin
+ case (adr)
+ // cstart:
+ 10'h000: data = 4'h1; // ldi 9
+ 10'h001: data = 4'h9;
+ 10'h002: data = 4'h0;
+ 10'h003: data = 4'h0;
+ // cstart2:
+ 10'h004: data = 4'he; // wait
+ 10'h005: data = 4'h9; // bc connected
+ 10'h006: data = 4'h7;
+ 10'h007: data = 4'h2;
+ 10'h008: data = 4'h8; // bz cstart
+ 10'h009: data = 4'h0;
+ 10'h00a: data = 4'h0;
+ 10'h00b: data = 4'h1; // ldi 200
+ 10'h00c: data = 4'h8;
+ 10'h00d: data = 4'hc;
+ 10'h00e: data = 4'h0;
+ 10'h00f: data = 4'h0;
+ // w200ms:
+ 10'h010: data = 4'he; // wait
+ 10'h011: data = 4'hb; // djnz w200ms
+ 10'h012: data = 4'h4;
+ 10'h013: data = 4'h0;
+ 10'h014: data = 4'h4; // out0
+ 10'h015: data = 4'h1; // ldi 10
+ 10'h016: data = 4'ha;
+ 10'h017: data = 4'h0;
+ // busrstlp:
+ 10'h018: data = 4'he; // wait
+ 10'h019: data = 4'hb; // djnz busrstlp
+ 10'h01a: data = 4'h6;
+ 10'h01b: data = 4'h0;
+ 10'h01c: data = 4'h5; // hiz
+ 10'h01d: data = 4'h1; // ldi 40
+ 10'h01e: data = 4'h8;
+ 10'h01f: data = 4'h2;
+ // w40ms:
+ 10'h020: data = 4'he; // wait
+ 10'h021: data = 4'h3; // out4 0x03
+ 10'h022: data = 4'h3;
+ 10'h023: data = 4'h0;
+ 10'h024: data = 4'h5; // hiz
+ 10'h025: data = 4'hb; // djnz w40ms
+ 10'h026: data = 4'h8;
+ 10'h027: data = 4'h0;
+ 10'h028: data = 4'he; // wait
+ 10'h029: data = 4'hf; // jmp setadr1
+ 10'h02a: data = 4'h1;
+ 10'h02b: data = 4'h3;
+ 10'h02c: data = 4'h0;
+ 10'h02d: data = 4'h5; // hiz
+ 10'h02e: data = 4'hf; // jmp rcvdt
+ 10'h02f: data = 4'hb;
+ 10'h030: data = 4'h5;
+ 10'h031: data = 4'h0;
+ 10'h032: data = 4'h0;
+ 10'h033: data = 4'h0;
+ // sendinlp:
+ 10'h034: data = 4'hf; // jmp in00
+ 10'h035: data = 4'h0;
+ 10'h036: data = 4'h6;
+ 10'h037: data = 4'h0;
+ 10'h038: data = 4'h5; // hiz
+ 10'h039: data = 4'hf; // jmp rcvdt
+ 10'h03a: data = 4'hb;
+ 10'h03b: data = 4'h5;
+ 10'h03c: data = 4'h0;
+ 10'h03d: data = 4'ha; // bnak sendinlp
+ 10'h03e: data = 4'hd;
+ 10'h03f: data = 4'h0;
+ 10'h040: data = 4'hf; // jmp sendack
+ 10'h041: data = 4'hc;
+ 10'h042: data = 4'h6;
+ 10'h043: data = 4'h0;
+ 10'h044: data = 4'h5; // hiz
+ 10'h045: data = 4'he; // wait
+ 10'h046: data = 4'hf; // jmp getdesc
+ 10'h047: data = 4'hf;
+ 10'h048: data = 4'h3;
+ 10'h049: data = 4'h0;
+ 10'h04a: data = 4'h5; // hiz
+ 10'h04b: data = 4'hf; // jmp rcvdt
+ 10'h04c: data = 4'hb;
+ 10'h04d: data = 4'h5;
+ 10'h04e: data = 4'h0;
+ 10'h04f: data = 4'h0;
+ // wait_desc:
+ 10'h050: data = 4'hf; // jmp in10
+ 10'h051: data = 4'h4;
+ 10'h052: data = 4'h6;
+ 10'h053: data = 4'h0;
+ 10'h054: data = 4'h5; // hiz
+ 10'h055: data = 4'hf; // jmp rcvdt
+ 10'h056: data = 4'hb;
+ 10'h057: data = 4'h5;
+ 10'h058: data = 4'h0;
+ 10'h059: data = 4'ha; // bnak wait_desc
+ 10'h05a: data = 4'h4;
+ 10'h05b: data = 4'h1;
+ 10'h05c: data = 4'hf; // jmp sendack
+ 10'h05d: data = 4'hc;
+ 10'h05e: data = 4'h6;
+ 10'h05f: data = 4'h0;
+ 10'h060: data = 4'h5; // hiz
+ 10'h061: data = 4'h0;
+ 10'h062: data = 4'h0;
+ 10'h063: data = 4'h0;
+ // wait_desc2:
+ 10'h064: data = 4'hf; // jmp in10
+ 10'h065: data = 4'h4;
+ 10'h066: data = 4'h6;
+ 10'h067: data = 4'h0;
+ 10'h068: data = 4'h5; // hiz
+ 10'h069: data = 4'hf; // jmp rcvdt
+ 10'h06a: data = 4'hb;
+ 10'h06b: data = 4'h5;
+ 10'h06c: data = 4'h0;
+ 10'h06d: data = 4'ha; // bnak wait_desc2
+ 10'h06e: data = 4'h9;
+ 10'h06f: data = 4'h1;
+ 10'h070: data = 4'hc; // vidpid
+ 10'h071: data = 4'h2;
+ 10'h072: data = 4'hf; // jmp sendack
+ 10'h073: data = 4'hc;
+ 10'h074: data = 4'h6;
+ 10'h075: data = 4'h0;
+ 10'h076: data = 4'h5; // hiz
+ 10'h077: data = 4'he; // wait
+ 10'h078: data = 4'hf; // jmp setconfig1
+ 10'h079: data = 4'hd;
+ 10'h07a: data = 4'h4;
+ 10'h07b: data = 4'h0;
+ 10'h07c: data = 4'h5; // hiz
+ 10'h07d: data = 4'hf; // jmp rcvdt
+ 10'h07e: data = 4'hb;
+ 10'h07f: data = 4'h5;
+ 10'h080: data = 4'h0;
+ 10'h081: data = 4'h0;
+ 10'h082: data = 4'h0;
+ 10'h083: data = 4'h0;
+ // in10lp:
+ 10'h084: data = 4'hf; // jmp in10
+ 10'h085: data = 4'h4;
+ 10'h086: data = 4'h6;
+ 10'h087: data = 4'h0;
+ 10'h088: data = 4'h5; // hiz
+ 10'h089: data = 4'hf; // jmp rcvdt
+ 10'h08a: data = 4'hb;
+ 10'h08b: data = 4'h5;
+ 10'h08c: data = 4'h0;
+ 10'h08d: data = 4'ha; // bnak in10lp
+ 10'h08e: data = 4'h1;
+ 10'h08f: data = 4'h2;
+ 10'h090: data = 4'hf; // jmp sendack
+ 10'h091: data = 4'hc;
+ 10'h092: data = 4'h6;
+ 10'h093: data = 4'h0;
+ 10'h094: data = 4'h5; // hiz
+ 10'h095: data = 4'hc; // toggle
+ 10'h096: data = 4'h1;
+ 10'h097: data = 4'hf; // jmp cstart
+ 10'h098: data = 4'h0;
+ 10'h099: data = 4'h0;
+ 10'h09a: data = 4'h0;
+ 10'h09b: data = 4'h0;
+ // connected:
+ 10'h09c: data = 4'h8; // bz connerr
+ 10'h09d: data = 4'hf;
+ 10'h09e: data = 4'h2;
+ 10'h09f: data = 4'h3; // out4 0x03
+ 10'h0a0: data = 4'h3;
+ 10'h0a1: data = 4'h0;
+ 10'h0a2: data = 4'h5; // hiz
+ 10'h0a3: data = 4'hb; // djnz cstart2
+ 10'h0a4: data = 4'h1;
+ 10'h0a5: data = 4'h0;
+ 10'h0a6: data = 4'he; // wait
+ 10'h0a7: data = 4'hf; // jmp in11
+ 10'h0a8: data = 4'h8;
+ 10'h0a9: data = 4'h6;
+ 10'h0aa: data = 4'h0;
+ 10'h0ab: data = 4'h5; // hiz
+ 10'h0ac: data = 4'hf; // jmp rcvdt
+ 10'h0ad: data = 4'hb;
+ 10'h0ae: data = 4'h5;
+ 10'h0af: data = 4'h0;
+ 10'h0b0: data = 4'ha; // bnak cstart
+ 10'h0b1: data = 4'h0;
+ 10'h0b2: data = 4'h0;
+ 10'h0b3: data = 4'hf; // jmp sendack
+ 10'h0b4: data = 4'hc;
+ 10'h0b5: data = 4'h6;
+ 10'h0b6: data = 4'h0;
+ 10'h0b7: data = 4'h5; // hiz
+ 10'h0b8: data = 4'hf; // jmp cstart
+ 10'h0b9: data = 4'h0;
+ 10'h0ba: data = 4'h0;
+ 10'h0bb: data = 4'h0;
+ // connerr:
+ 10'h0bc: data = 4'hc; // toggle
+ 10'h0bd: data = 4'h1;
+ 10'h0be: data = 4'hf; // jmp cstart
+ 10'h0bf: data = 4'h0;
+ 10'h0c0: data = 4'h0;
+ 10'h0c1: data = 4'h0;
+ 10'h0c2: data = 4'h0;
+ 10'h0c3: data = 4'h0;
+ // setadr1:
+ 10'h0c4: data = 4'h6; // outb 0x80
+ 10'h0c5: data = 4'h0;
+ 10'h0c6: data = 4'h8;
+ 10'h0c7: data = 4'h6; // outb 0x2d
+ 10'h0c8: data = 4'hd;
+ 10'h0c9: data = 4'h2;
+ 10'h0ca: data = 4'h6; // outb 0x00
+ 10'h0cb: data = 4'h0;
+ 10'h0cc: data = 4'h0;
+ 10'h0cd: data = 4'h6; // outb 0x10
+ 10'h0ce: data = 4'h0;
+ 10'h0cf: data = 4'h1;
+ 10'h0d0: data = 4'h3; // out4 0x03
+ 10'h0d1: data = 4'h3;
+ 10'h0d2: data = 4'h0;
+ 10'h0d3: data = 4'h6; // outb 0x80
+ 10'h0d4: data = 4'h0;
+ 10'h0d5: data = 4'h8;
+ 10'h0d6: data = 4'h6; // outb 0xc3
+ 10'h0d7: data = 4'h3;
+ 10'h0d8: data = 4'hc;
+ 10'h0d9: data = 4'h6; // outb 0x00
+ 10'h0da: data = 4'h0;
+ 10'h0db: data = 4'h0;
+ 10'h0dc: data = 4'h6; // outb 0x05
+ 10'h0dd: data = 4'h5;
+ 10'h0de: data = 4'h0;
+ 10'h0df: data = 4'h6; // outb 0x01
+ 10'h0e0: data = 4'h1;
+ 10'h0e1: data = 4'h0;
+ 10'h0e2: data = 4'h6; // outb 0x00
+ 10'h0e3: data = 4'h0;
+ 10'h0e4: data = 4'h0;
+ 10'h0e5: data = 4'h6; // outb 0x00
+ 10'h0e6: data = 4'h0;
+ 10'h0e7: data = 4'h0;
+ 10'h0e8: data = 4'h6; // outb 0x00
+ 10'h0e9: data = 4'h0;
+ 10'h0ea: data = 4'h0;
+ 10'h0eb: data = 4'h6; // outb 0x00
+ 10'h0ec: data = 4'h0;
+ 10'h0ed: data = 4'h0;
+ 10'h0ee: data = 4'h6; // outb 0x00
+ 10'h0ef: data = 4'h0;
+ 10'h0f0: data = 4'h0;
+ 10'h0f1: data = 4'h6; // outb 0xeb
+ 10'h0f2: data = 4'hb;
+ 10'h0f3: data = 4'he;
+ 10'h0f4: data = 4'h6; // outb 0x25
+ 10'h0f5: data = 4'h5;
+ 10'h0f6: data = 4'h2;
+ 10'h0f7: data = 4'h3; // out4 0x03
+ 10'h0f8: data = 4'h3;
+ 10'h0f9: data = 4'h0;
+ 10'h0fa: data = 4'h7; // ret
+ 10'h0fb: data = 4'h0;
+ // getdesc:
+ 10'h0fc: data = 4'h6; // outb 0x80
+ 10'h0fd: data = 4'h0;
+ 10'h0fe: data = 4'h8;
+ 10'h0ff: data = 4'h6; // outb 0x2d
+ 10'h100: data = 4'hd;
+ 10'h101: data = 4'h2;
+ 10'h102: data = 4'h6; // outb 0x01
+ 10'h103: data = 4'h1;
+ 10'h104: data = 4'h0;
+ 10'h105: data = 4'h6; // outb 0xe8
+ 10'h106: data = 4'h8;
+ 10'h107: data = 4'he;
+ 10'h108: data = 4'h3; // out4 0x03
+ 10'h109: data = 4'h3;
+ 10'h10a: data = 4'h0;
+ 10'h10b: data = 4'h6; // outb 0x80
+ 10'h10c: data = 4'h0;
+ 10'h10d: data = 4'h8;
+ 10'h10e: data = 4'h6; // outb 0xc3
+ 10'h10f: data = 4'h3;
+ 10'h110: data = 4'hc;
+ 10'h111: data = 4'h6; // outb 0x80
+ 10'h112: data = 4'h0;
+ 10'h113: data = 4'h8;
+ 10'h114: data = 4'h6; // outb 0x06
+ 10'h115: data = 4'h6;
+ 10'h116: data = 4'h0;
+ 10'h117: data = 4'h6; // outb 0x00
+ 10'h118: data = 4'h0;
+ 10'h119: data = 4'h0;
+ 10'h11a: data = 4'h6; // outb 0x01
+ 10'h11b: data = 4'h1;
+ 10'h11c: data = 4'h0;
+ 10'h11d: data = 4'h6; // outb 0x00
+ 10'h11e: data = 4'h0;
+ 10'h11f: data = 4'h0;
+ 10'h120: data = 4'h6; // outb 0x00
+ 10'h121: data = 4'h0;
+ 10'h122: data = 4'h0;
+ 10'h123: data = 4'h6; // outb 0x12
+ 10'h124: data = 4'h2;
+ 10'h125: data = 4'h1;
+ 10'h126: data = 4'h6; // outb 0x00
+ 10'h127: data = 4'h0;
+ 10'h128: data = 4'h0;
+ 10'h129: data = 4'h6; // outb 0xE0
+ 10'h12a: data = 4'h0;
+ 10'h12b: data = 4'he;
+ 10'h12c: data = 4'h6; // outb 0xF4
+ 10'h12d: data = 4'h4;
+ 10'h12e: data = 4'hf;
+ 10'h12f: data = 4'h3; // out4 0x03
+ 10'h130: data = 4'h3;
+ 10'h131: data = 4'h0;
+ 10'h132: data = 4'h7; // ret
+ 10'h133: data = 4'h0;
+ // setconfig1:
+ 10'h134: data = 4'h6; // outb 0x80
+ 10'h135: data = 4'h0;
+ 10'h136: data = 4'h8;
+ 10'h137: data = 4'h6; // outb 0x2d
+ 10'h138: data = 4'hd;
+ 10'h139: data = 4'h2;
+ 10'h13a: data = 4'h6; // outb 0x01
+ 10'h13b: data = 4'h1;
+ 10'h13c: data = 4'h0;
+ 10'h13d: data = 4'h6; // outb 0xe8
+ 10'h13e: data = 4'h8;
+ 10'h13f: data = 4'he;
+ 10'h140: data = 4'h3; // out4 0x03
+ 10'h141: data = 4'h3;
+ 10'h142: data = 4'h0;
+ 10'h143: data = 4'h6; // outb 0x80
+ 10'h144: data = 4'h0;
+ 10'h145: data = 4'h8;
+ 10'h146: data = 4'h6; // outb 0xc3
+ 10'h147: data = 4'h3;
+ 10'h148: data = 4'hc;
+ 10'h149: data = 4'h6; // outb 0x00
+ 10'h14a: data = 4'h0;
+ 10'h14b: data = 4'h0;
+ 10'h14c: data = 4'h6; // outb 0x09
+ 10'h14d: data = 4'h9;
+ 10'h14e: data = 4'h0;
+ 10'h14f: data = 4'h6; // outb 0x01
+ 10'h150: data = 4'h1;
+ 10'h151: data = 4'h0;
+ 10'h152: data = 4'h6; // outb 0x00
+ 10'h153: data = 4'h0;
+ 10'h154: data = 4'h0;
+ 10'h155: data = 4'h6; // outb 0x00
+ 10'h156: data = 4'h0;
+ 10'h157: data = 4'h0;
+ 10'h158: data = 4'h6; // outb 0x00
+ 10'h159: data = 4'h0;
+ 10'h15a: data = 4'h0;
+ 10'h15b: data = 4'h6; // outb 0x00
+ 10'h15c: data = 4'h0;
+ 10'h15d: data = 4'h0;
+ 10'h15e: data = 4'h6; // outb 0x00
+ 10'h15f: data = 4'h0;
+ 10'h160: data = 4'h0;
+ 10'h161: data = 4'h6; // outb 0x27
+ 10'h162: data = 4'h7;
+ 10'h163: data = 4'h2;
+ 10'h164: data = 4'h6; // outb 0x25
+ 10'h165: data = 4'h5;
+ 10'h166: data = 4'h2;
+ 10'h167: data = 4'h3; // out4 0x03
+ 10'h168: data = 4'h3;
+ 10'h169: data = 4'h0;
+ 10'h16a: data = 4'h7; // ret
+ 10'h16b: data = 4'h0;
+ // rcvdt:
+ 10'h16c: data = 4'h1; // ldi 104
+ 10'h16d: data = 4'h8;
+ 10'h16e: data = 4'h6;
+ 10'h16f: data = 4'h2; // start
+ 10'h170: data = 4'hd; // in
+ 10'h171: data = 4'h0;
+ 10'h172: data = 4'h0;
+ 10'h173: data = 4'h0;
+ // rcvdt2:
+ 10'h174: data = 4'h1; // ldi 2
+ 10'h175: data = 4'h2;
+ 10'h176: data = 4'h0;
+ 10'h177: data = 4'h0;
+ // rcvdt3:
+ 10'h178: data = 4'h8; // bz rcvdt2
+ 10'h179: data = 4'hd;
+ 10'h17a: data = 4'h5;
+ 10'h17b: data = 4'hb; // djnz rcvdt3
+ 10'h17c: data = 4'he;
+ 10'h17d: data = 4'h5;
+ 10'h17e: data = 4'h7; // ret
+ 10'h17f: data = 4'h0;
+ // in00:
+ 10'h180: data = 4'h6; // outb 0x80
+ 10'h181: data = 4'h0;
+ 10'h182: data = 4'h8;
+ 10'h183: data = 4'h6; // outb 0x69
+ 10'h184: data = 4'h9;
+ 10'h185: data = 4'h6;
+ 10'h186: data = 4'h6; // outb 0x00
+ 10'h187: data = 4'h0;
+ 10'h188: data = 4'h0;
+ 10'h189: data = 4'h6; // outb 0x10
+ 10'h18a: data = 4'h0;
+ 10'h18b: data = 4'h1;
+ 10'h18c: data = 4'h3; // out4 0x03
+ 10'h18d: data = 4'h3;
+ 10'h18e: data = 4'h0;
+ 10'h18f: data = 4'h7; // ret
+ // in10:
+ 10'h190: data = 4'h6; // outb 0x80
+ 10'h191: data = 4'h0;
+ 10'h192: data = 4'h8;
+ 10'h193: data = 4'h6; // outb 0x69
+ 10'h194: data = 4'h9;
+ 10'h195: data = 4'h6;
+ 10'h196: data = 4'h6; // outb 0x01
+ 10'h197: data = 4'h1;
+ 10'h198: data = 4'h0;
+ 10'h199: data = 4'h6; // outb 0xe8
+ 10'h19a: data = 4'h8;
+ 10'h19b: data = 4'he;
+ 10'h19c: data = 4'h3; // out4 0x03
+ 10'h19d: data = 4'h3;
+ 10'h19e: data = 4'h0;
+ 10'h19f: data = 4'h7; // ret
+ // in11:
+ 10'h1a0: data = 4'h6; // outb 0x80
+ 10'h1a1: data = 4'h0;
+ 10'h1a2: data = 4'h8;
+ 10'h1a3: data = 4'h6; // outb 0x69
+ 10'h1a4: data = 4'h9;
+ 10'h1a5: data = 4'h6;
+ 10'h1a6: data = 4'h6; // outb 0x81
+ 10'h1a7: data = 4'h1;
+ 10'h1a8: data = 4'h8;
+ 10'h1a9: data = 4'h6; // outb 0x58
+ 10'h1aa: data = 4'h8;
+ 10'h1ab: data = 4'h5;
+ 10'h1ac: data = 4'h3; // out4 0x03
+ 10'h1ad: data = 4'h3;
+ 10'h1ae: data = 4'h0;
+ 10'h1af: data = 4'h7; // ret
+ // sendack:
+ 10'h1b0: data = 4'h6; // outb 0x80
+ 10'h1b1: data = 4'h0;
+ 10'h1b2: data = 4'h8;
+ 10'h1b3: data = 4'h6; // outb 0xd2
+ 10'h1b4: data = 4'h2;
+ 10'h1b5: data = 4'hd;
+ 10'h1b6: data = 4'h3; // out4 0x03
+ 10'h1b7: data = 4'h3;
+ 10'h1b8: data = 4'h0;
+ 10'h1b9: data = 4'h7; // ret
+ 10'h1ba: data = 4'h0;
+ 10'h1bb: data = 4'h0;
+ // prgend:
+ default: data = 4'hX;
+ endcase
+ end
+endmodule
diff --git a/tools/crc.py b/tools/crc.py
new file mode 100644
index 0000000..2b86a15
--- /dev/null
+++ b/tools/crc.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python3
+
+import sys
+
+def usage():
+ print('USB CRC calculator')
+ print(' crc.py -5 calculate CRC-5-USB of {byte0[0:7], byte1[0:2]} (11 bits)')
+ print(' crc.py -16 ... calculcate CRC-16 of these bytes')
+ print('All bytes should be in hex')
+ exit(0)
+
+if len(sys.argv) < 2 or sys.argv[1] != '-5' and sys.argv[1] != '-16':
+ usage()
+
+def crc5(data):
+ # x^5 + x^2 + 1
+ polynomial = 0b00101
+ # USB crc5 is initialized to all 1
+ register = 0b11111
+
+ if len(data) != 2 and len(data) != 3:
+ usage()
+
+ for d in range(len(data)):
+ byte = data[d]
+ bits = range(8) if d < len(data)-1 else range(3)
+ # print(bits)
+ for i in bits:
+ bit = (byte >> i) & 1
+ xor_flag = (register >> 4) & 1
+ register = (register << 1) & 0b11111
+ if bit ^ xor_flag:
+ register ^= polynomial
+
+ # USB crc5 is inversed at output time
+ return 0b11111-register
+
+def crc16(data):
+ return 0
+
+bs = []
+
+for i in range(2, len(sys.argv)):
+ bs.append(int(sys.argv[i], 16))
+
+# bs = [0b10101010, 0b01010101, 0b11110000]
+print("data={}".format(bs))
+
+if sys.argv[1] == '-5':
+ # actually printed from MSB to LSB
+ print("crc5={:05b}".format(crc5(bs)))
+else:
+ print("crc16={}".format(crc16(bs)))
+
+
+
+