|
//
|
|
// user_io.v
|
|
//
|
|
// user_io for the MiST board
|
|
// http://code.google.com/p/mist-board/
|
|
//
|
|
// Copyright (c) 2014 Till Harbaum <till@harbaum.org>
|
|
//
|
|
// This source file is free software: you can redistribute it and/or modify
|
|
// it under the terms of the Lesser 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/>.
|
|
//
|
|
|
|
// parameter STRLEN and the actual length of conf_str have to match
|
|
|
|
module user_io #(parameter STRLEN=0) (
|
|
input [(8*STRLEN)-1:0] conf_str,
|
|
|
|
input SPI_CLK,
|
|
input SPI_SS_IO,
|
|
output reg SPI_MISO,
|
|
input SPI_MOSI,
|
|
|
|
output reg [5:0] joystick_0,
|
|
output reg [5:0] joystick_1,
|
|
output reg [15:0] joystick_analog_0,
|
|
output reg [15:0] joystick_analog_1,
|
|
output [1:0] buttons,
|
|
output [1:0] switches,
|
|
|
|
output reg [7:0] status,
|
|
|
|
// connection to sd card emulation
|
|
input [31:0] sd_lba,
|
|
input sd_rd,
|
|
input sd_wr,
|
|
output reg sd_ack,
|
|
input sd_conf,
|
|
input sd_sdhc,
|
|
output reg [7:0] sd_dout,
|
|
output reg sd_dout_strobe,
|
|
input [7:0] sd_din,
|
|
output reg sd_din_strobe,
|
|
|
|
|
|
// ps2 keyboard emulation
|
|
input ps2_clk, // 12-16khz provided by core
|
|
output ps2_kbd_clk,
|
|
output reg ps2_kbd_data,
|
|
|
|
// serial com port
|
|
input [7:0] serial_data,
|
|
input serial_strobe
|
|
);
|
|
|
|
reg [6:0] sbuf;
|
|
reg [7:0] cmd;
|
|
reg [2:0] bit_cnt; // counts bits 0-7 0-7 ...
|
|
reg [7:0] byte_cnt; // counts bytes
|
|
reg [5:0] joystick0;
|
|
reg [5:0] joystick1;
|
|
reg [3:0] but_sw;
|
|
reg [2:0] stick_idx;
|
|
|
|
assign buttons = but_sw[1:0];
|
|
assign switches = but_sw[3:2];
|
|
|
|
// this variant of user_io is for 8 bit cores (type == a4) only
|
|
wire [7:0] core_type = 8'ha4;
|
|
|
|
// command byte read by the io controller
|
|
wire [7:0] sd_cmd = { 4'h5, sd_conf, sd_sdhc, sd_wr, sd_rd };
|
|
|
|
// filter spi clock. the 8 bit gate delay is ~2.5ns in total
|
|
wire [7:0] spi_sck_D = { spi_sck_D[6:0], SPI_CLK } /* synthesis keep */;
|
|
wire spi_sck = (spi_sck && spi_sck_D != 8'h00) || (!spi_sck && spi_sck_D == 8'hff);
|
|
|
|
// drive MISO only when transmitting core id
|
|
always@(negedge spi_sck or posedge SPI_SS_IO) begin
|
|
if(SPI_SS_IO == 1) begin
|
|
SPI_MISO <= 1'bZ;
|
|
end else begin
|
|
|
|
// first byte returned is always core type, further bytes are
|
|
// command dependent
|
|
if(byte_cnt == 0) begin
|
|
SPI_MISO <= core_type[~bit_cnt];
|
|
|
|
end else begin
|
|
// reading serial fifo
|
|
if(cmd == 8'h1b) begin
|
|
// send alternating flag byte and data
|
|
if(byte_cnt[0]) SPI_MISO <= serial_out_status[~bit_cnt];
|
|
else SPI_MISO <= serial_out_byte[~bit_cnt];
|
|
end
|
|
|
|
// reading config string
|
|
else if(cmd == 8'h14) begin
|
|
// returning a byte from string
|
|
if(byte_cnt < STRLEN + 1)
|
|
SPI_MISO <= conf_str[{STRLEN - byte_cnt,~bit_cnt}];
|
|
else
|
|
SPI_MISO <= 1'b0;
|
|
end
|
|
|
|
// reading sd card status
|
|
else if(cmd == 8'h16) begin
|
|
if(byte_cnt == 1)
|
|
SPI_MISO <= sd_cmd[~bit_cnt];
|
|
else if((byte_cnt >= 2) && (byte_cnt < 6))
|
|
SPI_MISO <= sd_lba[{5-byte_cnt, ~bit_cnt}];
|
|
else
|
|
SPI_MISO <= 1'b0;
|
|
end
|
|
|
|
// reading sd card write data
|
|
else if(cmd == 8'h18)
|
|
SPI_MISO <= sd_din[~bit_cnt];
|
|
|
|
else
|
|
SPI_MISO <= 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
// 8 byte fifo to store ps2 bytes
|
|
localparam PS2_FIFO_BITS = 3;
|
|
reg [7:0] ps2_fifo [(2**PS2_FIFO_BITS)-1:0];
|
|
reg [PS2_FIFO_BITS-1:0] ps2_wptr;
|
|
reg [PS2_FIFO_BITS-1:0] ps2_rptr;
|
|
|
|
// ps2 transmitter state machine
|
|
reg [3:0] ps2_tx_state;
|
|
reg [7:0] ps2_tx_byte;
|
|
reg ps2_parity;
|
|
|
|
assign ps2_kbd_clk = ps2_clk || (ps2_tx_state == 0);
|
|
|
|
// ps2 transmitter
|
|
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
|
|
reg ps2_r_inc;
|
|
always@(posedge ps2_clk) begin
|
|
ps2_r_inc <= 1'b0;
|
|
|
|
if(ps2_r_inc)
|
|
ps2_rptr <= ps2_rptr + 1;
|
|
|
|
// transmitter is idle?
|
|
if(ps2_tx_state == 0) begin
|
|
// data in fifo present?
|
|
if(ps2_wptr != ps2_rptr) begin
|
|
// load tx register from fifo
|
|
ps2_tx_byte <= ps2_fifo[ps2_rptr];
|
|
ps2_r_inc <= 1'b1;
|
|
|
|
// reset parity
|
|
ps2_parity <= 1'b1;
|
|
|
|
// start transmitter
|
|
ps2_tx_state <= 4'd1;
|
|
|
|
// put start bit on data line
|
|
ps2_kbd_data <= 1'b0; // start bit is 0
|
|
end
|
|
end else begin
|
|
|
|
// transmission of 8 data bits
|
|
if((ps2_tx_state >= 1)&&(ps2_tx_state < 9)) begin
|
|
ps2_kbd_data <= ps2_tx_byte[0]; // data bits
|
|
ps2_tx_byte[6:0] <= ps2_tx_byte[7:1]; // shift down
|
|
if(ps2_tx_byte[0])
|
|
ps2_parity <= !ps2_parity;
|
|
end
|
|
|
|
// transmission of parity
|
|
if(ps2_tx_state == 9)
|
|
ps2_kbd_data <= ps2_parity;
|
|
|
|
// transmission of stop bit
|
|
if(ps2_tx_state == 10)
|
|
ps2_kbd_data <= 1'b1; // stop bit is 1
|
|
|
|
// advance state machine
|
|
if(ps2_tx_state < 11)
|
|
ps2_tx_state <= ps2_tx_state + 4'd1;
|
|
else
|
|
ps2_tx_state <= 4'd0;
|
|
|
|
end
|
|
end
|
|
|
|
// fifo to receive serial data from core to be forwarded to io controller
|
|
|
|
// 16 byte fifo to store serial bytes
|
|
localparam SERIAL_OUT_FIFO_BITS = 6;
|
|
reg [7:0] serial_out_fifo [(2**SERIAL_OUT_FIFO_BITS)-1:0];
|
|
reg [SERIAL_OUT_FIFO_BITS-1:0] serial_out_wptr;
|
|
reg [SERIAL_OUT_FIFO_BITS-1:0] serial_out_rptr;
|
|
|
|
wire serial_out_data_available = serial_out_wptr != serial_out_rptr;
|
|
wire [7:0] serial_out_byte = serial_out_fifo[serial_out_rptr] /* synthesis keep */;
|
|
wire [7:0] serial_out_status = { 7'b1000000, serial_out_data_available};
|
|
|
|
// status[0] is reset signal from io controller and is thus used to flush
|
|
// the fifo
|
|
always @(posedge serial_strobe or posedge status[0]) begin
|
|
if(status[0] == 1) begin
|
|
serial_out_wptr <= 0;
|
|
end else begin
|
|
serial_out_fifo[serial_out_wptr] <= serial_data;
|
|
serial_out_wptr <= serial_out_wptr + 1;
|
|
end
|
|
end
|
|
|
|
always@(negedge spi_sck or posedge status[0]) begin
|
|
if(status[0] == 1) begin
|
|
serial_out_rptr <= 0;
|
|
end else begin
|
|
if((byte_cnt != 0) && (cmd == 8'h1b)) begin
|
|
// read last bit -> advance read pointer
|
|
if((bit_cnt == 7) && !byte_cnt[0] && serial_out_data_available)
|
|
serial_out_rptr <= serial_out_rptr + 1;
|
|
end
|
|
end
|
|
end
|
|
|
|
// SPI receiver
|
|
always@(posedge spi_sck or posedge SPI_SS_IO) begin
|
|
|
|
if(SPI_SS_IO == 1) begin
|
|
bit_cnt <= 3'd0;
|
|
byte_cnt <= 8'd0;
|
|
sd_ack <= 1'b0;
|
|
sd_dout_strobe <= 1'b0;
|
|
sd_din_strobe <= 1'b0;
|
|
end else begin
|
|
sd_dout_strobe <= 1'b0;
|
|
sd_din_strobe <= 1'b0;
|
|
|
|
sbuf[6:0] <= { sbuf[5:0], SPI_MOSI };
|
|
bit_cnt <= bit_cnt + 3'd1;
|
|
if((bit_cnt == 7)&&(byte_cnt != 8'd255))
|
|
byte_cnt <= byte_cnt + 8'd1;
|
|
|
|
// finished reading command byte
|
|
if(bit_cnt == 7) begin
|
|
if(byte_cnt == 0) begin
|
|
cmd <= { sbuf, SPI_MOSI};
|
|
|
|
// fetch first byte when sectore FPGA->IO command has been seen
|
|
if({ sbuf, SPI_MOSI} == 8'h18)
|
|
sd_din_strobe <= 1'b1;
|
|
|
|
if(({ sbuf, SPI_MOSI} == 8'h17) || ({ sbuf, SPI_MOSI} == 8'h18))
|
|
sd_ack <= 1'b1;
|
|
|
|
end else begin
|
|
|
|
// buttons and switches
|
|
if(cmd == 8'h01)
|
|
but_sw <= { sbuf[2:0], SPI_MOSI };
|
|
|
|
if(cmd == 8'h02)
|
|
joystick_0 <= { sbuf[4:0], SPI_MOSI };
|
|
|
|
if(cmd == 8'h03)
|
|
joystick_1 <= { sbuf[4:0], SPI_MOSI };
|
|
|
|
if(cmd == 8'h05) begin
|
|
// store incoming keyboard bytes in
|
|
ps2_fifo[ps2_wptr] <= { sbuf, SPI_MOSI };
|
|
ps2_wptr <= ps2_wptr + 1;
|
|
end
|
|
|
|
if(cmd == 8'h15)
|
|
status <= { sbuf[6:0], SPI_MOSI };
|
|
|
|
// send sector IO -> FPGA
|
|
if(cmd == 8'h17) begin
|
|
// flag that download begins
|
|
sd_dout <= { sbuf, SPI_MOSI};
|
|
sd_dout_strobe <= 1'b1;
|
|
end
|
|
|
|
// send sector FPGA -> IO
|
|
if(cmd == 8'h18)
|
|
sd_din_strobe <= 1'b1;
|
|
|
|
// send SD config IO -> FPGA
|
|
if(cmd == 8'h19) begin
|
|
// flag that download begins
|
|
sd_dout <= { sbuf, SPI_MOSI};
|
|
// sd card knows data is config if sd_dout_strobe is asserted
|
|
// with sd_ack still being inactive (low)
|
|
sd_dout_strobe <= 1'b1;
|
|
end
|
|
|
|
// joystick analog
|
|
if(cmd == 8'h1a) begin
|
|
// first byte is joystick indes
|
|
if(byte_cnt == 1)
|
|
stick_idx <= { sbuf[1:0], SPI_MOSI };
|
|
else if(byte_cnt == 2) begin
|
|
// second byte is x axis
|
|
if(stick_idx == 0)
|
|
joystick_analog_0[15:8] <= { sbuf, SPI_MOSI };
|
|
else if(stick_idx == 1)
|
|
joystick_analog_1[15:8] <= { sbuf, SPI_MOSI };
|
|
end else if(byte_cnt == 3) begin
|
|
// third byte is y axis
|
|
if(stick_idx == 0)
|
|
joystick_analog_0[7:0] <= { sbuf, SPI_MOSI };
|
|
else if(stick_idx == 1)
|
|
joystick_analog_1[7:0] <= { sbuf, SPI_MOSI };
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|