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))) + + + +