Files
SharpMZ/sys/hps_io.v
Philip Smart 4a64af4a00 Initial commit
2019-10-25 17:16:34 +01:00

1097 lines
38 KiB
Verilog

//
// hps_io.v
//
// mist_io-like module for MiSTer
//
// Copyright (c) 2014 Till Harbaum <till@harbaum.org>
// Copyright (c) 2017,2018 Sorgelig
// SharpMZ series specific updates made by Philip Smart, 2018.
//
// 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 <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////
//
// 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,
//toggle to force notify of video mode change
input new_vmode,
// 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 ioctl_upload = 0, // signal indicating an active upload
output reg [7:0] ioctl_index, // menu index used to upload the file
output reg ioctl_wr,
output reg ioctl_rd,
output reg [24:0] ioctl_addr, // in WIDE mode address will be incremented by 2
output reg [15:0] ioctl_dout,
input [15:0] ioctl_din,
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, old_vmode = 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
old_vmode <= new_vmode;
if(vid_hcnt != hcnt || vid_vcnt != vcnt || old_vmode != new_vmode) 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
///////////////////////////////// HPS I/O ////////////////////////////////////
localparam UIO_FILE_TX = 8'h53;
localparam UIO_FILE_TX_DAT = 8'h54;
localparam UIO_FILE_INDEX = 8'h55;
localparam UIO_FILE_INFO = 8'h56;
localparam UIO_FILE_ADDR = 8'h57;
localparam UIO_FILE_ADDR_TX = 8'h58;
localparam UIO_FILE_ADDR_RX = 8'h59;
localparam UIO_CONFIG_RX = 8'h5A;
localparam UIO_CONFIG_TX = 8'h5B;
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));
// Primary loop for HPS <-> FPGA I/O. Look at sys_top.v in the HPS I/O section
// for the ack pulse going back to the HPS when data is written.
//
// Basic mode of operation is the HPS writes a byte on io_din and reads back
// io_dout within the same clock cycle (clk_sys). io_enable is set active when
// an EnableFPGA is executed within the HPS Main MiSTer program. io_strobe is
// set active by the HPS when it writes a byte (sys_top.v acknowledges the
// strobe) and reset within the same clk_sys cycle.
//
always@(posedge clk_sys) begin
reg [15:0] cmd;
reg [15:0] byte_cnt; // counts bytes
reg [2:0] b_wr;
reg [2:0] stick_idx;
reg ps2skip = 0;
reg [24:0] addr; // Address signal to hps logic in emulator.
reg wr; // Write signal to hps logic in emulator.
reg rd; // Read signal to hps logic in emulator.
// After the UIO_FILE_ADDR_TX command, wr will be set to 1, this is then transferred
// to the wire ioctl_wr at next clock cycle (1 cycle after address setup) and will be held
// high for 1 clock cycle of clk_sys. This provides the setup and hold time for the data
// before the write signal is generated.
//
ioctl_wr <= wr;
wr <= 0;
// For read, the UIO_FILE_ADDR_RX command places the wire ioctl_rd to 1 on the clock
// immediately follwing the command along with the address. It is held
// high for 2 cycles, on the 2nd cycle the data is sampled. The read data
// belongs to the previous address as the HPS mechanism writes data
// synchronised with the sys_clk and reads back within that same cycle
// (ie. 1x sys_clk). Thus the HPS needs to write a 0 @ addr1 then write another
// 0 @ addr2 and the data read back belongs to addr1.
//
ioctl_rd <= rd;
if(ioctl_rd == 1) begin
io_dout <= ioctl_din[15:0]; // This read is for previous address setup.
end
rd <= 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;
addr <= 0;
ioctl_upload <= 0;
ioctl_download <= 0;
ioctl_rd <= 0;
ioctl_wr <= 0;
end
else
begin
if(io_strobe)
begin
// Reset the input bus as needed.
if(cmd != UIO_CONFIG_RX)
begin
io_dout <= 0;
end
// Increment but dont let the byte counter roll-over.
if(~&byte_cnt) byte_cnt <= byte_cnt + 1'd1;
// First byte after strobe is the command, store and setup for
// command processing.
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;
UIO_FILE_ADDR: // Direct setup/change of address, each byte becomes the lower 7-0 bits of address.
begin
addr <= (addr << 8) | io_din[7:0];
end
UIO_FILE_INDEX: // Setup of index (0-255), for index addressing.
begin
ioctl_index <= io_din[7:0];
end
UIO_FILE_TX: // Standard file download, address starts at 0.
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: // File data download, byte sent to output bus and wr is pulsed (for 1 clock after address/data setup).
begin
ioctl_addr <= addr;
ioctl_dout <= io_din[15:0];
wr <= 1;
addr <= addr + (WIDE ? 2'd2 : 2'd1);
end
UIO_FILE_ADDR_TX: // Modified file download, starting address is sent.
begin
// Address - 1=:24, 1 = 23:16, 2 = 15:8, 3 = 7:0
if(byte_cnt < 5)
begin
addr <= (addr << 8) | io_din[7:0];
// LSB byte signifies start of data receipt.
if(byte_cnt == 4)
begin
ioctl_download <= 1;
end
end
else
begin
ioctl_addr <= addr;
ioctl_dout <= io_din[15:0];
wr <= 1;
addr <= addr + (WIDE ? 2'd2 : 2'd1);
end
end
UIO_FILE_ADDR_RX: // Modified file upload, starting address is sent.
begin
// Address - 1=:24, 1 = 23:16, 2 = 15:8, 3 = 7:0
if(byte_cnt < 5)
begin
addr <= (addr << 8) | io_din[7:0];
// 4th byte signifies address received, upload
// commences. Due to timings of an IO read, we
// set up the address and read signals
// 1 period before it is read, so at byte 4,
// we setup first read which occurs at byte 5.
//
if(byte_cnt == 4)
begin
ioctl_addr <= (addr << 8) | io_din[7:0];
ioctl_upload <= 1;
ioctl_rd <= 1;
rd <= 1;
end
end
else
begin
// Normally the HPS sets io_din to 0 during
// receive operation. If it is set to non-zero
// the this is the end of upload marker.
//
//if(io_din[7:0]) begin
// ioctl_upload <= 0;
// ioctl_rd <= 0;
//end
//else
//begin
ioctl_addr <= addr;
ioctl_rd <= 1;
rd <= 1;
addr <= addr + (WIDE ? 2'd2 : 2'd1);
//end
end
end
UIO_CONFIG_RX:
begin
if(byte_cnt == 1)
begin
ioctl_addr <= ({21'b100000000000000000000, io_din[3:0]});
ioctl_rd <= 1;
rd <= 1;
//ioctl_upload <= 1;
end
//else
//begin
// if(io_din[7:0]) begin
// ioctl_upload <= 0;
// ioctl_rd <= 0;
// end
//end
io_dout <= ioctl_din[DW:0]; // This read is for previous address setup.
end
UIO_CONFIG_TX:
begin
case(byte_cnt)
1: begin
ioctl_addr <= ({21'b100000000000000000000, io_din[3:0]});
end
2: begin
ioctl_dout <= io_din[DW:0];
wr <= 1;
end
endcase
end
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)
);
endmodule
//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 [24:0] addr;
// 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;
// addr <= 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;
//
// // Read emulator memory, main task performed in download
// // block, this component just puts the data onto the
// // io_dout bus.
// 'h59: io_dout <= 'h55; //ioctl_din[DW:0];
// endcase
// end
// end
// end
//end
//localparam UIO_FILE_TX = 8'h53;
//localparam UIO_FILE_TX_DAT = 8'h54;
//localparam UIO_FILE_INDEX = 8'h55;
//localparam UIO_FILE_INFO = 8'h56;
//localparam UIO_FILE_ADDR = 8'h57;
//localparam UIO_FILE_RX = 8'h58;
//localparam UIO_FILE_RX_DAT = 8'h59;
//
//always@(posedge clk_sys) begin
// reg [15:0] cmd;
// reg has_cmd;
// reg [24:0] addr;
// reg wr;
// reg rd;
//
// ioctl_wr <= wr;
// ioctl_rd <= rd;
// wr <= 0;
// rd <= 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_ADDR: // Direct setup of address, each byte becomes the lower 7-0 bits of address.
// begin
// addr <= (addr << 8) | io_din[7:0];
// end
//
// 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
//
// UIO_FILE_RX:
// begin
// if(io_din[7:0]) begin
// addr <= 0;
// ioctl_upload <= 1;
// end else begin
// ioctl_addr <= addr;
// ioctl_upload <= 0;
// end
// end
//
// UIO_FILE_RX_DAT:
// begin
// ioctl_addr <= addr;
// rd <= 1;
// addr <= addr + (WIDE ? 2'd2 : 2'd1);
// end
// endcase
// end
// end
// end
//end
//////////////////////////////////////////////////////////////////////////////////
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<<PS2_FIFO_BITS];
reg [PS2_FIFO_BITS-1:0] wptr;
reg [PS2_FIFO_BITS-1:0] rptr;
reg [2:0] rx_state = 0;
reg [3:0] tx_state = 0;
reg has_data;
reg [7:0] data;
assign rdata = {has_data, data};
always@(posedge clk_sys) begin
reg [7:0] tx_byte;
reg parity;
reg r_inc;
reg old_clk;
reg [1:0] timeout;
reg [3:0] rx_cnt;
reg c1,c2,d1;
tx_empty <= ((wptr == rptr) && (tx_state == 0));
if(we) begin
fifo[wptr] <= wdata;
wptr <= wptr + 1'd1;
end
if(rd) has_data <= 0;
c1 <= ps2_clk_in;
c2 <= c1;
d1 <= ps2_dat_in;
if(!rx_state && !tx_state && ~c2 && c1 && ~d1) begin
rx_state <= rx_state + 1'b1;
ps2_dat_out <= 1;
end
old_clk <= ps2_clk;
if(~old_clk & ps2_clk) begin
if(rx_state) begin
case(rx_state)
1: begin
rx_state <= rx_state + 1'b1;
rx_cnt <= 0;
end
2: begin
if(rx_cnt <= 7) data <= {d1, data[7:1]};
else rx_state <= rx_state + 1'b1;
rx_cnt <= rx_cnt + 1'b1;
end
3: if(d1) begin
rx_state <= rx_state + 1'b1;
ps2_dat_out <= 0;
end
4: begin
ps2_dat_out <= 1;
has_data <= 1;
rx_state <= 0;
end
endcase
end else begin
// transmitter is idle?
if(tx_state == 0) begin
// data in fifo present?
if(c2 && c1 && d1 && wptr != rptr) begin
timeout <= timeout - 1'd1;
if(!timeout) begin
tx_byte <= fifo[rptr];
rptr <= rptr + 1'd1;
// reset parity
parity <= 1;
// start transmitter
tx_state <= 1;
// put start bit on data line
ps2_dat_out <= 0; // start bit is 0
end
end
end else begin
// transmission of 8 data bits
if((tx_state >= 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