// // hps_io.v // // mist_io-like module for the Terasic DE10 board // // Copyright (c) 2014 Till Harbaum // Copyright (c) 2017 Sorgelig (port to DE10-nano) // // This source file is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This source file is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////// // // Use buffer to access SD card. It's time-critical part. // // for synchronous projects default value for PS2DIV is fine for any frequency of system clock. // clk_ps2 = CLK_SYS/(PS2DIV*2) // // WIDE=1 for 16 bit file I/O // VDNUM 1-4 module hps_io #(parameter STRLEN=0, PS2DIV=2000, WIDE=0, VDNUM=1, PS2WE=0) ( input clk_sys, inout [44:0] HPS_BUS, // parameter STRLEN and the actual length of conf_str have to match input [(8*STRLEN)-1:0] conf_str, output reg [15:0] joystick_0, output reg [15:0] joystick_1, output reg [15:0] joystick_analog_0, output reg [15:0] joystick_analog_1, output [1:0] buttons, output forced_scandoubler, output reg [31:0] status, // SD config output reg [VD:0] img_mounted, // signaling that new image has been mounted output reg img_readonly, // mounted as read only. valid only for active bit in img_mounted output reg [63:0] img_size, // size of image in bytes. valid only for active bit in img_mounted // SD block level access input [31:0] sd_lba, input [VD:0] sd_rd, // only single sd_rd can be active at any given time input [VD:0] sd_wr, // only single sd_wr can be active at any given time output reg sd_ack, // do not use in new projects. // CID and CSD are fake except CSD image size field. input sd_conf, output reg sd_ack_conf, // SD byte level access. Signals for 2-PORT altsyncram. output reg [AW:0] sd_buff_addr, output reg [DW:0] sd_buff_dout, input [DW:0] sd_buff_din, output reg sd_buff_wr, // ARM -> FPGA download output reg ioctl_download = 0, // signal indicating an active download output reg [7:0] ioctl_index, // menu index used to upload the file output reg ioctl_wr, output reg [24:0] ioctl_addr, // in WIDE mode address will be incremented by 2 output reg [DW:0] ioctl_dout, input ioctl_wait, // RTC MSM6242B layout output reg [64:0] RTC, // Seconds since 1970-01-01 00:00:00 output reg [32:0] TIMESTAMP, // ps2 keyboard emulation output ps2_kbd_clk_out, output ps2_kbd_data_out, input ps2_kbd_clk_in, input ps2_kbd_data_in, input [2:0] ps2_kbd_led_status, input [2:0] ps2_kbd_led_use, output ps2_mouse_clk_out, output ps2_mouse_data_out, input ps2_mouse_clk_in, input ps2_mouse_data_in, // ps2 alternative interface. // [8] - extended, [9] - pressed, [10] - toggles with every press/release output reg [10:0] ps2_key = 0, // [24] - toggles with every event output reg [24:0] ps2_mouse = 0 ); localparam DW = (WIDE) ? 15 : 7; localparam AW = (WIDE) ? 7 : 8; localparam VD = VDNUM-1; wire io_wait = ioctl_wait; wire io_enable= |HPS_BUS[35:34]; wire io_strobe= HPS_BUS[33]; wire io_wide = (WIDE) ? 1'b1 : 1'b0; wire [15:0] io_din = HPS_BUS[31:16]; reg [15:0] io_dout; assign HPS_BUS[37] = io_wait; assign HPS_BUS[36] = clk_sys; assign HPS_BUS[32] = io_wide; assign HPS_BUS[15:0] = io_dout; reg [7:0] cfg; assign buttons = cfg[1:0]; //cfg[2] - vga_scaler handled in sys_top //cfg[3] - csync handled in sys_top assign forced_scandoubler = cfg[4]; //cfg[5] - ypbpr handled in sys_top // command byte read by the io controller wire [15:0] sd_cmd = { 2'b00, (VDNUM>=4) ? sd_wr[3] : 1'b0, (VDNUM>=3) ? sd_wr[2] : 1'b0, (VDNUM>=2) ? sd_wr[1] : 1'b0, (VDNUM>=4) ? sd_rd[3] : 1'b0, (VDNUM>=3) ? sd_rd[2] : 1'b0, (VDNUM>=2) ? sd_rd[1] : 1'b0, 4'h5, sd_conf, 1'b1, sd_wr[0], sd_rd[0] }; ///////////////// calc video parameters ////////////////// wire clk_100 = HPS_BUS[43]; wire clk_vid = HPS_BUS[42]; wire ce_pix = HPS_BUS[41]; wire de = HPS_BUS[40]; wire hs = HPS_BUS[39]; wire vs = HPS_BUS[38]; wire vs_hdmi = HPS_BUS[44]; reg [31:0] vid_hcnt = 0; reg [31:0] vid_vcnt = 0; reg [7:0] vid_nres = 0; integer hcnt; always @(posedge clk_vid) begin integer vcnt; reg old_vs= 0, old_de = 0; reg calch = 0; if(ce_pix) begin old_vs <= vs; old_de <= de; if(~vs & ~old_de & de) vcnt <= vcnt + 1; if(calch & de) hcnt <= hcnt + 1; if(old_de & ~de) calch <= 0; if(old_vs & ~vs) begin if(hcnt && vcnt) begin if(vid_hcnt != hcnt || vid_vcnt != vcnt) vid_nres <= vid_nres + 1'd1; vid_hcnt <= hcnt; vid_vcnt <= vcnt; end vcnt <= 0; hcnt <= 0; calch <= 1; end end end reg [31:0] vid_htime = 0; reg [31:0] vid_vtime = 0; reg [31:0] vid_pix = 0; always @(posedge clk_100) begin integer vtime, htime, hcnt; reg old_vs, old_hs, old_vs2, old_hs2, old_de, old_de2; reg calch = 0; old_vs <= vs; old_hs <= hs; old_vs2 <= old_vs; old_hs2 <= old_hs; vtime <= vtime + 1'd1; htime <= htime + 1'd1; if(~old_vs2 & old_vs) begin vid_pix <= hcnt; vid_vtime <= vtime; vtime <= 0; hcnt <= 0; end if(old_vs2 & ~old_vs) calch <= 1; if(~old_hs2 & old_hs) begin vid_htime <= htime; htime <= 0; end old_de <= de; old_de2 <= old_de; if(calch & old_de) hcnt <= hcnt + 1; if(old_de2 & ~old_de) calch <= 0; end reg [31:0] vid_vtime_hdmi; always @(posedge clk_100) begin integer vtime; reg old_vs, old_vs2; old_vs <= vs_hdmi; old_vs2 <= old_vs; vtime <= vtime + 1'd1; if(~old_vs2 & old_vs) begin vid_vtime_hdmi <= vtime; vtime <= 0; end end ///////////////////////////////////////////////////////// reg [31:0] ps2_key_raw = 0; wire pressed = (ps2_key_raw[15:8] != 8'hf0); wire extended = (~pressed ? (ps2_key_raw[23:16] == 8'he0) : (ps2_key_raw[15:8] == 8'he0)); always@(posedge clk_sys) begin reg [15:0] cmd; reg [9:0] byte_cnt; // counts bytes reg [2:0] b_wr; reg [2:0] stick_idx; reg ps2skip = 0; sd_buff_wr <= b_wr[0]; if(b_wr[2] && (~&sd_buff_addr)) sd_buff_addr <= sd_buff_addr + 1'b1; b_wr <= (b_wr<<1); {kbd_rd,kbd_we,mouse_rd,mouse_we} <= 0; if(~io_enable) begin if(cmd == 4 && !ps2skip) ps2_mouse[24] <= ~ps2_mouse[24]; if(cmd == 5 && !ps2skip) begin ps2_key <= {~ps2_key[10], pressed, extended, ps2_key_raw[7:0]}; if(ps2_key_raw == 'hE012E07C) ps2_key[9:0] <= 'h37C; // prnscr pressed if(ps2_key_raw == 'h7CE0F012) ps2_key[9:0] <= 'h17C; // prnscr released if(ps2_key_raw == 'hF014F077) ps2_key[9:0] <= 'h377; // pause pressed end if(cmd == 'h22) RTC[64] <= ~RTC[64]; if(cmd == 'h24) TIMESTAMP[32] <= ~TIMESTAMP[32]; cmd <= 0; byte_cnt <= 0; sd_ack <= 0; sd_ack_conf <= 0; io_dout <= 0; ps2skip <= 0; end else begin if(io_strobe) begin io_dout <= 0; if(~&byte_cnt) byte_cnt <= byte_cnt + 1'd1; if(byte_cnt == 0) begin cmd <= io_din; case(io_din) 'h19: sd_ack_conf <= 1; 'h17, 'h18: sd_ack <= 1; endcase sd_buff_addr <= 0; img_mounted <= 0; if(io_din == 5) ps2_key_raw <= 0; end else begin case(cmd) // buttons and switches 'h01: cfg <= io_din[7:0]; 'h02: joystick_0 <= io_din; 'h03: joystick_1 <= io_din; // store incoming ps2 mouse bytes 'h04: begin mouse_data <= io_din[7:0]; mouse_we <= 1; if(&io_din[15:8]) ps2skip <= 1; if(~&io_din[15:8] & ~ps2skip) begin case(byte_cnt) 1: ps2_mouse[7:0] <= io_din[7:0]; 2: ps2_mouse[15:8] <= io_din[7:0]; 3: ps2_mouse[23:16] <= io_din[7:0]; endcase end end // store incoming ps2 keyboard bytes 'h05: begin if(&io_din[15:8]) ps2skip <= 1; if(~&io_din[15:8] & ~ps2skip) ps2_key_raw[31:0] <= {ps2_key_raw[23:0], io_din[7:0]}; kbd_data <= io_din[7:0]; kbd_we <= 1; end // reading config string 'h14: begin // returning a byte from string if(byte_cnt < STRLEN + 1) io_dout[7:0] <= conf_str[(STRLEN - byte_cnt)<<3 +:8]; end // reading sd card status 'h16: begin case(byte_cnt) 1: io_dout <= sd_cmd; 2: io_dout <= sd_lba[15:0]; 3: io_dout <= sd_lba[31:16]; endcase end // send SD config IO -> FPGA // flag that download begins // sd card knows data is config if sd_dout_strobe is asserted // with sd_ack still being inactive (low) 'h19, // send sector IO -> FPGA // flag that download begins 'h17: begin sd_buff_dout <= io_din[DW:0]; b_wr <= 1; end // reading sd card write data 'h18: begin if(~&sd_buff_addr) sd_buff_addr <= sd_buff_addr + 1'b1; io_dout <= sd_buff_din; end // joystick analog 'h1a: begin // first byte is joystick index if(byte_cnt == 1) stick_idx <= io_din[2:0]; if(byte_cnt == 2) begin if(stick_idx == 0) joystick_analog_0 <= io_din; if(stick_idx == 1) joystick_analog_1 <= io_din; end end // notify image selection 'h1c: begin img_mounted <= io_din[VD:0] ? io_din[VD:0] : 1'b1; img_readonly <= io_din[7]; end // send image info 'h1d: if(byte_cnt<5) img_size[{byte_cnt-1'b1, 4'b0000} +:16] <= io_din; // status, 32bit version 'h1e: if(byte_cnt==1) status[15:0] <= io_din; else if(byte_cnt==2) status[31:16] <= io_din; // reading keyboard LED status 'h1f: io_dout <= {|PS2WE, 2'b01, ps2_kbd_led_status[2], ps2_kbd_led_use[2], ps2_kbd_led_status[1], ps2_kbd_led_use[1], ps2_kbd_led_status[0], ps2_kbd_led_use[0]}; // reading ps2 keyboard/mouse control 'h21: begin if(byte_cnt == 1) begin io_dout <= kbd_data_host; kbd_rd <= 1; end if(byte_cnt == 2) begin io_dout <= mouse_data_host; mouse_rd <= 1; end end //RTC 'h22: RTC[(byte_cnt-6'd1)<<4 +:16] <= io_din; //Video res. 'h23: begin case(byte_cnt) 1: io_dout <= vid_nres; 2: io_dout <= vid_hcnt[15:0]; 3: io_dout <= vid_hcnt[31:16]; 4: io_dout <= vid_vcnt[15:0]; 5: io_dout <= vid_vcnt[31:16]; 6: io_dout <= vid_htime[15:0]; 7: io_dout <= vid_htime[31:16]; 8: io_dout <= vid_vtime[15:0]; 9: io_dout <= vid_vtime[31:16]; 10: io_dout <= vid_pix[15:0]; 11: io_dout <= vid_pix[31:16]; 12: io_dout <= vid_vtime_hdmi[15:0]; 13: io_dout <= vid_vtime_hdmi[31:16]; endcase end //RTC 'h24: TIMESTAMP[(byte_cnt-6'd1)<<4 +:16] <= io_din; endcase end end end end /////////////////////////////// PS2 /////////////////////////////// reg clk_ps2; always @(negedge clk_sys) begin integer cnt; cnt <= cnt + 1'd1; if(cnt == PS2DIV) begin clk_ps2 <= ~clk_ps2; cnt <= 0; end end reg [7:0] kbd_data; reg kbd_we; wire [8:0] kbd_data_host; reg kbd_rd; ps2_device keyboard ( .clk_sys(clk_sys), .wdata(kbd_data), .we(kbd_we), .ps2_clk(clk_ps2), .ps2_clk_out(ps2_kbd_clk_out), .ps2_dat_out(ps2_kbd_data_out), .ps2_clk_in(ps2_kbd_clk_in || !PS2WE), .ps2_dat_in(ps2_kbd_data_in || !PS2WE), .rdata(kbd_data_host), .rd(kbd_rd) ); reg [7:0] mouse_data; reg mouse_we; wire [8:0] mouse_data_host; reg mouse_rd; ps2_device mouse ( .clk_sys(clk_sys), .wdata(mouse_data), .we(mouse_we), .ps2_clk(clk_ps2), .ps2_clk_out(ps2_mouse_clk_out), .ps2_dat_out(ps2_mouse_data_out), .ps2_clk_in(ps2_mouse_clk_in || !PS2WE), .ps2_dat_in(ps2_mouse_data_in || !PS2WE), .rdata(mouse_data_host), .rd(mouse_rd) ); /////////////////////////////// DOWNLOADING /////////////////////////////// localparam UIO_FILE_TX = 8'h53; localparam UIO_FILE_TX_DAT = 8'h54; localparam UIO_FILE_INDEX = 8'h55; always@(posedge clk_sys) begin reg [15:0] cmd; reg has_cmd; reg [24:0] addr; reg wr; ioctl_wr <= wr; wr <= 0; if(~io_enable) has_cmd <= 0; else begin if(io_strobe) begin if(!has_cmd) begin cmd <= io_din; has_cmd <= 1; end else begin case(cmd) UIO_FILE_INDEX: begin ioctl_index <= io_din[7:0]; end UIO_FILE_TX: begin if(io_din[7:0]) begin addr <= 0; ioctl_download <= 1; end else begin ioctl_addr <= addr; ioctl_download <= 0; end end UIO_FILE_TX_DAT: begin ioctl_addr <= addr; ioctl_dout <= io_din[DW:0]; wr <= 1; addr <= addr + (WIDE ? 2'd2 : 2'd1); end endcase end end end end endmodule ////////////////////////////////////////////////////////////////////////////////// module ps2_device #(parameter PS2_FIFO_BITS=5) ( input clk_sys, input [7:0] wdata, input we, input ps2_clk, output reg ps2_clk_out, output reg ps2_dat_out, output reg tx_empty, input ps2_clk_in, input ps2_dat_in, output [8:0] rdata, input rd ); (* ramstyle = "logic" *) reg [7:0] fifo[1<= 1)&&(tx_state < 9)) begin ps2_dat_out <= tx_byte[0]; // data bits tx_byte[6:0] <= tx_byte[7:1]; // shift down if(tx_byte[0]) parity <= !parity; end // transmission of parity if(tx_state == 9) ps2_dat_out <= parity; // transmission of stop bit if(tx_state == 10) ps2_dat_out <= 1; // stop bit is 1 // advance state machine if(tx_state < 11) tx_state <= tx_state + 1'd1; else tx_state <= 0; end end end if(~old_clk & ps2_clk) ps2_clk_out <= 1; if(old_clk & ~ps2_clk) ps2_clk_out <= ((tx_state == 0) && (rx_state<2)); end endmodule