1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-01-17 08:33:16 +00:00

Update MiST modules

This commit is contained in:
Gyorgy Szombathelyi 2022-10-09 18:13:55 +02:00
parent 05c5117d3f
commit fe94350806
11 changed files with 1335 additions and 397 deletions

View File

@ -7,8 +7,10 @@ The modules:
- user_io.v - communicating with the IO controller.
- data_io.v - handling file uploads from the IO controller.
- mist_video.v - a video pipeline, which gives an optional scandoubler, OSD and rgb2ypbpr conversion.
- osd.v, scandoubler.v, rgb2ypbpr.sv, cofi.sv - these are used in mist_video, but can be used separately, too.
- osd.v, scandoubler.v, rgb2ypbpr.v, cofi.sv - these are used in mist_video, but can be used separately, too.
- sd_card.v - gives an SPI interface with SD-Card commands towards the IO-Controller, accessing .VHD and other mounted files.
- ide.v, ide_fifo.v - a bridge between a CPU and the data_io module for IDE/ATAPI disks.
- cdda_fifo.v - a module which connects data_io with a DAC for CDDA playback.
- dac.vhd - a simple sigma-delta DAC for audio output.
- arcade_inputs.v - mostly for arcade-style games, gives access to the joysticks with MAME-style keyboard mapping.
- mist.vhd - VHDL component declarations for user_io and mist_video.

107
common/mist/cdda_fifo.v Normal file
View File

@ -0,0 +1,107 @@
//
// cdda_fifo.v
//
// CDDA FIFO for the MiST board
// https://github.com/mist-devel
//
// Copyright (c) 2022 Gyorgy Szombathelyi
//
// 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/>.
//
///////////////////////////////////////////////////////////////////////
module cdda_fifo
(
input clk_sys,
input clk_en, // set to 1 when using the stock data_io
input cen_44100, // 44100 HZ clock enable
input reset,
// data_io interface
output hdd_cdda_req,
input hdd_cdda_wr,
input [15:0] hdd_data_out,
// sample output
output reg [15:0] cdda_l,
output reg [15:0] cdda_r
);
// 4k x 16bit default FIFO size
parameter FIFO_DEPTH = 12;
reg [15:0] fifo[2**FIFO_DEPTH];
reg [FIFO_DEPTH-1:0] inptr;
reg [FIFO_DEPTH-1:0] outptr;
reg [15:0] fifo_out;
wire [FIFO_DEPTH:0] fifo_used = inptr >= outptr ?
inptr - outptr :
inptr - outptr + (2'd2**FIFO_DEPTH);
assign hdd_cdda_req = fifo_used < ((2'd2**FIFO_DEPTH) - 16'd2352);
always @(posedge clk_sys) begin
if (reset)
inptr <= 0;
else if (clk_en && hdd_cdda_wr) begin
fifo[inptr] <= {hdd_data_out[7:0], hdd_data_out[15:8]};
inptr <= inptr + 1'd1;
end
end
always @(posedge clk_sys) fifo_out <= fifo[outptr];
reg left = 0;
reg mute = 1;
reg fifo_active = 0;
always @(posedge clk_sys) begin
if (reset) begin
outptr <= 0;
fifo_active <= 0;
mute <= 1;
left <= 0;
cdda_l <= 0;
cdda_r <= 0;
end else begin
if (cen_44100) begin
if (fifo_used >= 2352)
fifo_active <= 1;
if (outptr + 2'd2 == inptr)
fifo_active <= 0;
if (fifo_active) begin
outptr <= outptr + 1'd1;
left <= 1;
mute <= 0;
end else
mute <= 1;
end
if (left) begin
outptr <= outptr + 1'd1;
left <= 0;
end
if (mute) begin
cdda_l <= 0;
cdda_r <= 0;
end else begin
if (left)
cdda_l <= fifo_out;
else
cdda_r <= fifo_out;
end
end
end
endmodule

View File

@ -2,7 +2,7 @@
// data_io.v
//
// data_io for the MiST board
// http://code.google.com/p/mist-board/
// https://github.com/mist-devel
//
// Copyright (c) 2014 Till Harbaum <till@harbaum.org>
//
@ -30,6 +30,10 @@ module data_io
input SPI_DI,
inout SPI_DO,
input QCSn,
input QSCK,
input [3:0] QDAT,
input clkref_n, // assert ioctl_wr one cycle after clkref stobe (negative active)
// ARM -> FPGA download
@ -43,52 +47,105 @@ module data_io
output reg [7:0] ioctl_dout,
input [7:0] ioctl_din,
output reg [23:0] ioctl_fileext, // file extension
output reg [31:0] ioctl_filesize // file size
output reg [31:0] ioctl_filesize, // file size
// IDE interface
input hdd_clk,
input hdd_cmd_req,
input hdd_cdda_req,
input hdd_dat_req,
output hdd_cdda_wr,
output hdd_status_wr,
output [2:0] hdd_addr = 0,
output hdd_wr,
output [15:0] hdd_data_out,
input [15:0] hdd_data_in,
output hdd_data_rd,
output hdd_data_wr,
// IDE config
output [1:0] hdd0_ena,
output [1:0] hdd1_ena
);
parameter START_ADDR = 25'd0;
parameter ROM_DIRECT_UPLOAD = 0;
parameter USE_QSPI = 0;
parameter ENABLE_IDE = 0;
/////////////////////////////// DOWNLOADING ///////////////////////////////
reg [6:0] sbuf;
reg [7:0] data_w;
reg [7:0] data_w2 = 0;
reg [7:0] data_w3 = 0;
reg [3:0] cnt;
reg [7:0] cmd;
reg [6:0] bytecnt;
reg rclk = 0;
reg rclk2 = 0;
reg rclk3 = 0;
reg addr_reset = 0;
reg downloading_reg = 0;
reg uploading_reg = 0;
reg reg_do;
localparam DIO_FILE_TX = 8'h53;
localparam DIO_FILE_TX_DAT = 8'h54;
localparam DIO_FILE_INDEX = 8'h55;
localparam DIO_FILE_INFO = 8'h56;
localparam DIO_FILE_RX = 8'h57;
localparam DIO_FILE_RX_DAT = 8'h58;
localparam DIO_FILE_TX = 8'h53;
localparam DIO_FILE_TX_DAT = 8'h54;
localparam DIO_FILE_INDEX = 8'h55;
localparam DIO_FILE_INFO = 8'h56;
localparam DIO_FILE_RX = 8'h57;
localparam DIO_FILE_RX_DAT = 8'h58;
localparam QSPI_READ = 8'h40;
localparam QSPI_WRITE = 8'h41;
localparam CMD_IDE_REGS_RD = 8'h80;
localparam CMD_IDE_REGS_WR = 8'h90;
localparam CMD_IDE_DATA_WR = 8'hA0;
localparam CMD_IDE_DATA_RD = 8'hB0;
localparam CMD_IDE_CDDA_RD = 8'hC0;
localparam CMD_IDE_CDDA_WR = 8'hD0;
localparam CMD_IDE_STATUS_WR = 8'hF0;
localparam CMD_IDE_CFG_WR = 8'hFA;
assign SPI_DO = reg_do;
// data_io has its own SPI interface to the io controller
wire [7:0] cmdcode = { 4'h0, hdd_dat_req, hdd_cmd_req, 2'b00 };
always@(negedge SPI_SCK or posedge SPI_SS2) begin : SPI_TRANSMITTER
reg [7:0] dout_r;
if(SPI_SS2) begin
reg_do <= 1'bZ;
end else begin
if (cnt == 15) dout_r <= ioctl_din;
if (cnt == 0) dout_r <= cmdcode;
if (cnt == 15) begin
case(cmd)
CMD_IDE_REGS_RD,
CMD_IDE_DATA_RD:
dout_r <= bytecnt[0] ? hdd_data_in[7:0] : hdd_data_in[15:8];
CMD_IDE_CDDA_RD:
dout_r <= {7'd0, hdd_cdda_req};
DIO_FILE_RX_DAT:
dout_r <= ioctl_din;
default:
dout_r <= 0;
endcase
end
reg_do <= dout_r[~cnt[2:0]];
end
end
always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER
reg [6:0] sbuf;
reg [24:0] addr;
reg [7:0] cmd;
reg [5:0] bytecnt;
if(SPI_SS2) begin
bytecnt <= 0;
cnt <= 0;
@ -105,6 +162,9 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER
if(cnt == 7) cmd <= {sbuf, SPI_DI};
if(cnt == 15) begin
if (~&bytecnt) bytecnt <= bytecnt + 1'd1;
else bytecnt[0] <= ~bytecnt[0];
case (cmd)
// prepare/end transmission
DIO_FILE_TX: begin
@ -140,7 +200,6 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER
// receiving FAT directory entry (mist-firmware/fat.h - DIRENTRY)
DIO_FILE_INFO: begin
bytecnt <= bytecnt + 1'd1;
case (bytecnt)
8'h08: ioctl_fileext[23:16] <= {sbuf, SPI_DI};
8'h09: ioctl_fileext[15: 8] <= {sbuf, SPI_DI};
@ -157,7 +216,7 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER
end
// direct SD Card->FPGA transfer
generate if (ROM_DIRECT_UPLOAD == 1) begin
generate if (ROM_DIRECT_UPLOAD || ENABLE_IDE) begin
always@(posedge SPI_SCK, posedge SPI_SS4) begin : SPI_DIRECT_RECEIVER
reg [6:0] sbuf2;
@ -192,19 +251,50 @@ end
end
endgenerate
// QSPI receiver
generate if (USE_QSPI) begin
always@(negedge QSCK, posedge QCSn) begin : QSPI_RECEIVER
reg nibble_lo;
reg cmd_got;
reg cmd_write;
if (QCSn) begin
cmd_got <= 0;
cmd_write <= 0;
nibble_lo <= 0;
end else begin
nibble_lo <= ~nibble_lo;
if (nibble_lo) begin
data_w3[3:0] <= QDAT;
if (!cmd_got) begin
cmd_got <= 1;
if ({data_w3[7:4], QDAT} == QSPI_WRITE) cmd_write <= 1;
end else begin
if (cmd_write) rclk3 <= ~rclk3;
end
end else
data_w3[7:4] <= QDAT;
end
end
end
endgenerate
always@(posedge clk_sys) begin : DATA_OUT
// synchronisers
reg rclkD, rclkD2;
reg rclk2D, rclk2D2;
reg rclk3D, rclk3D2;
reg addr_resetD, addr_resetD2;
reg wr_int, wr_int_direct, rd_int;
reg wr_int, wr_int_direct, wr_int_qspi, rd_int;
reg [24:0] addr;
reg [31:0] filepos;
// bring flags from spi clock domain into core clock domain
{ rclkD, rclkD2 } <= { rclk, rclkD };
{ rclk2D ,rclk2D2 } <= { rclk2, rclk2D };
{ rclk3D ,rclk3D2 } <= { rclk3, rclk3D };
{ addr_resetD, addr_resetD2 } <= { addr_reset, addr_resetD };
ioctl_wr <= 0;
@ -224,8 +314,9 @@ always@(posedge clk_sys) begin : DATA_OUT
rd_int <= 0;
wr_int <= 0;
wr_int_direct <= 0;
if (wr_int || wr_int_direct) begin
ioctl_dout <= wr_int ? data_w : data_w2;
wr_int_qspi <= 0;
if (wr_int || wr_int_direct || wr_int_qspi) begin
ioctl_dout <= wr_int ? data_w : wr_int_direct ? data_w2 : data_w3;
ioctl_wr <= 1;
addr <= addr + 1'd1;
ioctl_addr <= addr;
@ -250,10 +341,203 @@ always@(posedge clk_sys) begin : DATA_OUT
rd_int <= uploading_reg;
end
// direct transfer receiver
if (rclk2D ^ rclk2D2 && filepos != ioctl_filesize) begin
if (rclk2D ^ rclk2D2 && filepos != ioctl_filesize && downloading_reg) begin
filepos <= filepos + 1'd1;
wr_int_direct <= 1;
end
// QSPI transfer receiver
if (rclk3D ^ rclk3D2) begin
wr_int_qspi <= downloading_reg;
end
end
// IDE handling
generate if (ENABLE_IDE) begin
reg [1:0] int_hdd0_ena;
reg [1:0] int_hdd1_ena;
reg int_hdd_cdda_wr;
reg int_hdd_status_wr;
reg [2:0] int_hdd_addr = 0;
reg int_hdd_wr;
reg [15:0] int_hdd_data_out;
reg int_hdd_data_rd;
reg int_hdd_data_wr;
assign hdd0_ena = int_hdd0_ena;
assign hdd1_ena = int_hdd1_ena;
assign hdd_cdda_wr = int_hdd_cdda_wr;
assign hdd_status_wr = int_hdd_status_wr;
assign hdd_addr = int_hdd_addr;
assign hdd_wr = int_hdd_wr;
assign hdd_data_out = int_hdd_data_out;
assign hdd_data_rd = int_hdd_data_rd;
assign hdd_data_wr = int_hdd_data_wr;
reg rst0 = 1;
reg rst2 = 1;
reg rclk_ide_stat = 0;
reg rclk_ide_regs_rd = 0;
reg rclk_ide_regs_wr = 0;
reg rclk_ide_wr = 0;
reg rclk_ide_rd = 0;
reg rclk_cdda_wr = 0;
reg [7:0] data_ide;
always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER_IDE
if(SPI_SS2) begin
rst0 <= 1;
end else begin
rst0 <= 0;
if(cnt == 15) begin
case (cmd)
//IDE commands
CMD_IDE_CFG_WR:
if (bytecnt == 0) begin
int_hdd0_ena <= {sbuf[0], SPI_DI};
int_hdd1_ena <= sbuf[2:1];
end
CMD_IDE_STATUS_WR:
if (bytecnt == 0) begin
data_ide <= {sbuf, SPI_DI};
rclk_ide_stat <= ~rclk_ide_stat;
end
CMD_IDE_REGS_WR:
if (bytecnt >= 8 && bytecnt <= 18 && !bytecnt[0]) begin
data_ide <= {sbuf, SPI_DI};
rclk_ide_regs_wr <= ~rclk_ide_regs_wr;
end
CMD_IDE_REGS_RD:
if (bytecnt > 5 && !bytecnt[0]) begin
rclk_ide_regs_rd <= ~rclk_ide_regs_rd;
end
CMD_IDE_DATA_WR:
if (bytecnt > 4) begin
data_ide <= {sbuf, SPI_DI};
rclk_ide_wr <= ~rclk_ide_wr;
end
CMD_IDE_CDDA_WR:
if (bytecnt > 4) begin
data_ide <= {sbuf, SPI_DI};
rclk_cdda_wr <= ~rclk_cdda_wr;
end
CMD_IDE_DATA_RD:
if (bytecnt > 3) rclk_ide_rd <= ~rclk_ide_rd;
endcase
end
end
end
always@(posedge SPI_SCK, posedge SPI_SS4) begin : SPI_DIRECT_RECEIVER_IDE
if(SPI_SS4) begin
rst2 <= 1;
end else begin
rst2 <= 0;
end
end
always@(posedge hdd_clk) begin : IDE_OUT
reg loword;
// synchronisers
reg rclk2D, rclk2D2;
reg rclk_ide_statD, rclk_ide_statD2;
reg rclk_cdda_wrD, rclk_cdda_wrD2;
reg rclk_ide_wrD, rclk_ide_wrD2;
reg rclk_ide_rdD, rclk_ide_rdD2;
reg rclk_ide_regs_wrD, rclk_ide_regs_wrD2;
reg rclk_ide_regs_rdD, rclk_ide_regs_rdD2;
reg rst0D, rst0D2;
reg rst2D, rst2D2;
// bring flags from spi clock domain into core clock domain
{ rclk2D ,rclk2D2 } <= { rclk2, rclk2D };
{ rclk_ide_statD, rclk_ide_statD2 } <= { rclk_ide_stat, rclk_ide_statD };
{ rclk_ide_rdD, rclk_ide_rdD2 } <= { rclk_ide_rd, rclk_ide_rdD };
{ rclk_ide_wrD, rclk_ide_wrD2 } <= { rclk_ide_wr, rclk_ide_wrD };
{ rclk_cdda_wrD, rclk_cdda_wrD2 } <= { rclk_cdda_wr, rclk_cdda_wrD };
{ rclk_ide_regs_rdD, rclk_ide_regs_rdD2 } <= { rclk_ide_regs_rd, rclk_ide_regs_rdD };
{ rclk_ide_regs_wrD, rclk_ide_regs_wrD2 } <= { rclk_ide_regs_wr, rclk_ide_regs_wrD };
{ rst0D, rst0D2 } <= { rst0, rst0D };
{ rst2D, rst2D2 } <= { rst2, rst2D };
// IDE receiver
int_hdd_wr <= 0;
int_hdd_status_wr <= 0;
int_hdd_data_wr <= 0;
int_hdd_data_rd <= 0;
int_hdd_cdda_wr <= 0;
if (rst0D2) int_hdd_addr <= 0;
if (rst0D2 && rst2D2) loword <= 0;
if (rclk_ide_statD ^ rclk_ide_statD2) begin
int_hdd_status_wr <= 1;
int_hdd_data_out <= {8'h00, data_ide};
end
if (rclk_ide_rdD ^ rclk_ide_rdD2) begin
loword <= ~loword;
if (loword)
int_hdd_data_rd <= 1;
end
if (rclk_ide_wrD ^ rclk_ide_wrD2) begin
loword <= ~loword;
if (!loword)
int_hdd_data_out[15:8] <= data_ide;
else begin
int_hdd_data_wr <= 1;
int_hdd_data_out[7:0] <= data_ide;
end
end
if (rclk_cdda_wrD ^ rclk_cdda_wrD2) begin
loword <= ~loword;
if (!loword)
int_hdd_data_out[15:8] <= data_ide;
else begin
int_hdd_cdda_wr <= 1;
int_hdd_data_out[7:0] <= data_ide;
end
end
if (rclk2D ^ rclk2D2 && !downloading_reg) begin
loword <= ~loword;
if (!loword)
int_hdd_data_out[15:8] <= data_w2;
else begin
int_hdd_data_wr <= 1;
int_hdd_data_out[7:0] <= data_w2;
end
end
if (rclk_ide_regs_wrD ^ rclk_ide_regs_wrD2) begin
int_hdd_wr <= 1;
int_hdd_data_out <= {8'h00, data_ide};
int_hdd_addr <= int_hdd_addr + 1'd1;
end
if (rclk_ide_regs_rdD ^ rclk_ide_regs_rdD2) begin
int_hdd_addr <= int_hdd_addr + 1'd1;
end
end
end else begin
assign hdd0_ena = 0;
assign hdd1_ena = 0;
assign hdd_cdda_wr = 0;
assign hdd_status_wr = 0;
assign hdd_addr = 0;
assign hdd_wr = 0;
assign hdd_data_out = 0;
assign hdd_data_rd = 0;
assign hdd_data_wr = 0;
end
endgenerate
endmodule

405
common/mist/ide.v Normal file
View File

@ -0,0 +1,405 @@
// Copyright 2008, 2009 by Jakub Bednarski
//
// Extracted from Minimig gayle.v
//
// Minimig 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.
//
// Minimig 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/>.
//
//
//
// -- JB --
//
// 2008-10-06 - initial version
// 2008-10-08 - interrupt controller implemented, kickstart boots
// 2008-10-09 - working identify device command implemented (hdtoolbox detects our drive)
// - read command reads data from hardfile (fixed size and name, only one sector read size supported, workbench sees hardfile partition)
// 2008-10-10 - multiple sector transfer supported: works ok, sequential transfers with direct spi read and 28MHz CPU from 400 to 520 KB/s
// - arm firmare seekfile function very slow: seeking from start to 20MB takes 144 ms (some software improvements required)
// 2008-10-30 - write support added
// 2008-12-31 - added hdd enable
// 2009-05-24 - clean-up & renaming
// 2009-08-11 - hdd_ena enables Master & Slave drives
// 2009-11-18 - changed sector buffer size
// 2010-04-13 - changed sector buffer size
// 2010-08-10 - improved BSY signal handling
// 2022-08-18 - added packet command handling
module ide
(
input clk,
input clk_en,
input reset,
input [2:0] address_in,
input sel_secondary,
input [15:0] data_in,
output [15:0] data_out,
output data_oe,
input rd,
input hwr,
input lwr,
input sel_ide,
output reg [1:0] intreq,
input [1:0] intreq_ack, // interrupt clear
output nrdy, // fifo is not ready for reading
input [1:0] hdd0_ena, // enables Master & Slave drives on primary channel
input [1:0] hdd1_ena, // enables Master & Slave drives on secondary channel
output fifo_rd,
output fifo_wr,
// connection to the IO-Controller
output hdd_cmd_req,
output hdd_dat_req,
input [2:0] hdd_addr,
input [15:0] hdd_data_out,
output [15:0] hdd_data_in,
input hdd_wr,
input hdd_status_wr,
input hdd_data_wr,
input hdd_data_rd
);
localparam VCC = 1'b1;
localparam GND = 1'b0;
/*
0 Data
1 Error | Feature
2 SectorCount
3 SectorNumber
4 CylinderLow
5 CylinderHigh
6 Device/Head
7 Status | Command
command class:
PI (PIO In)
PO (PIO Out)
ND (No Data)
Status:
#6 - DRDY - Drive Ready
#7 - BSY - Busy
#3 - DRQ - Data Request
#0 - ERR - Error
INTRQ - Interrupt Request
*/
// address decoding signals
wire sel_tfr; // HDD task file registers select
wire sel_fifo; // HDD data port select (FIFO buffer)
wire sel_status /* synthesis keep */; // HDD status register select
wire sel_command /* synthesis keep */;// HDD command register select
// internal registers
reg block_mark; // IDE multiple block start flag
reg busy; // busy status (command processing state)
reg pio_in; // pio in command type is being processed
reg pio_out; // pio out command type is being processed
reg error; // error status (command processing failed)
reg [1:0] dev; // drive select (Primary/Secondary, Master/Slave)
wire bsy; // busy
wire drdy; // drive ready
wire drq; // data request
reg drq_d; // data request
wire err; // error
wire [7:0] status; // HDD status
// FIFO control
wire fifo_reset;
wire [15:0] fifo_data_in;
wire [15:0] fifo_data_out;
wire fifo_full;
wire fifo_empty;
wire fifo_last_out; // last word of a sector is being read
wire fifo_last_in; // last word of a sector is being written
// HDD status register
assign status = {bsy,drdy,2'b01,drq,2'b00,err};
// packet states
reg [1:0] packet_state;
localparam PACKET_IDLE = 0;
localparam PACKET_WAITCMD = 1;
localparam PACKET_PROCESSCMD = 2;
wire packet_state_change;
reg [12:0] packet_count;
wire packet_in_last;
wire packet_in;
wire packet_out;
`ifdef IDE_DEBUG
// cmd/status debug
reg [7:0] status_dbg /* synthesis noprune */;
reg [7:0] dbg_ide_cmd /* synthesis noprune */;
reg [2:0] dbg_addr /* synthesis noprune */;
reg dbg_wr /* synthesis noprune */;
reg[15:0] dbg_data_in /* synthesis noprune */;
reg[15:0] dbg_data_out /* synthesis noprune */;
always @(posedge clk) begin
status_dbg <= status;
if (clk_en) begin
dbg_wr <= 0;
if (sel_command) // set when the CPU writes command register
dbg_ide_cmd <= data_in[15:8];
if (sel_ide) begin
dbg_addr <= address_in;
dbg_wr <= hwr | lwr;
if (rd) dbg_data_out <= data_out;
if (hwr | lwr) dbg_data_in <= data_in;
end
end
end
`endif
// HDD status register bits
assign bsy = busy & ~drq;
assign drdy = ~(bsy|drq);
assign err = error;
// address decoding
assign sel_tfr = sel_ide;
assign sel_status = rd && sel_tfr && address_in==3'b111 ? VCC : GND;
assign sel_command = hwr && sel_tfr && address_in==3'b111 ? VCC : GND;
assign sel_fifo = sel_tfr && address_in==3'b000 ? VCC : GND;
//===============================================================================================//
// task file registers
reg [7:0] tfr [7:0];
wire [2:0] tfr_sel;
wire [7:0] tfr_in;
wire [7:0] tfr_out;
wire tfr_we;
reg [8:0] sector_count; // sector counter
wire sector_count_dec_in; // decrease sector counter (reads)
wire sector_count_dec_out; // decrease sector counter (writes)
always @(posedge clk)
if (clk_en) begin
if (hwr && sel_tfr && address_in == 3'b010) begin // sector count register loaded by the host
sector_count <= {1'b0, data_in[15:8]};
if (data_in[15:8] == 0) sector_count <= 9'd256;
end else if (sector_count_dec_in || sector_count_dec_out)
sector_count <= sector_count - 8'd1;
end
reg rd_old;
reg wr_old;
reg sel_fifo_old;
always @(posedge clk)
if (clk_en) begin
rd_old <= rd;
wr_old <= hwr & lwr;
sel_fifo_old <= sel_fifo;
end
assign sector_count_dec_in = pio_in & fifo_last_out & sel_fifo_old & ~rd & rd_old & packet_state == PACKET_IDLE;
assign sector_count_dec_out = pio_out & fifo_last_in & sel_fifo_old & ~hwr & ~lwr & wr_old & packet_state == PACKET_IDLE;
// task file register control
assign tfr_we = packet_in_last ? 1'b1 : bsy ? hdd_wr : sel_tfr & hwr;
assign tfr_sel = packet_in_last ? 3'd2 : bsy ? hdd_addr : address_in;
assign tfr_in = packet_in_last ? 8'h03: bsy ? hdd_data_out[7:0] : data_in[15:8];
// input multiplexer for SPI host
assign hdd_data_in = tfr_sel==0 ? fifo_data_out : {7'h0, dev[1], tfr_out};
// task file registers
always @(posedge clk)
if (clk_en) begin
if (tfr_we)
tfr[tfr_sel] <= tfr_in;
end
assign tfr_out = tfr[tfr_sel];
// master/slave drive select
always @(posedge clk)
if (clk_en) begin
if (reset)
dev <= 0;
else if (sel_tfr && address_in==6 && hwr)
dev <= {sel_secondary, data_in[12]};
end
assign packet_state_change = busy && hdd_status_wr && hdd_data_out[5];
// bytes count in a packet
always @(posedge clk)
if (clk_en) begin
if (reset)
packet_count <= 0;
else if (hdd_wr && hdd_addr == 4)
packet_count[6:0] <= hdd_data_out[7:1];
else if (hdd_wr && hdd_addr == 5)
packet_count[12:7] <= hdd_data_out[5:0];
else if (packet_state_change && packet_state == PACKET_IDLE)
packet_count <= 13'd6; // IDLE->WAITCMD transition, expect 6 words of packet command
end
// status register (write only from SPI host)
// 7 - busy status (write zero to finish command processing: allow host access to task file registers)
// 6
// 5
// 4 - intreq (used for writes only)
// 3 - drq enable for pio in (PI) command type
// 2 - drq enable for pio out (PO) command type
// 1
// 0 - error flag (remember about setting error task file register)
// command busy status
always @(posedge clk)
if (clk_en) begin
if (reset)
busy <= GND;
else if (hdd_status_wr && hdd_data_out[7] || (sector_count_dec_in && sector_count == 9'h01)) // reset by SPI host (by clearing BSY status bit)
busy <= GND;
else if (sel_command) // set when the CPU writes command register
busy <= VCC;
end
// IDE interrupt request register
always @(posedge clk)
if (clk_en) begin
drq_d <= drq;
if (reset) begin
intreq[0] <= GND;
intreq[1] <= GND;
block_mark <= GND;
end else begin
if (busy && hdd_status_wr && hdd_data_out[3])
block_mark <= VCC; // to handle IDENTIFY
if (pio_in) begin // reads
if (hdd_status_wr && hdd_data_out[4])
block_mark <= VCC;
if ((error | (!drq_d & drq)) & block_mark) begin
intreq[dev[1]] <= VCC;
block_mark <= GND;
end
if (packet_in_last) // read the last word from the packet command result
intreq[dev[1]] <= VCC;
end else if (pio_out) begin // writes
if (hdd_status_wr && hdd_data_out[4])
intreq[dev[1]] <= VCC;
end else if (hdd_status_wr && hdd_data_out[7]) // other command types completed
intreq[dev[1]] <= VCC;
else if (packet_state_change && packet_state == PACKET_IDLE) // ready to accept command packet
intreq[dev[1]] <= VCC;
if (intreq_ack[0]) intreq[0] <= GND; // cleared by the CPU
if (intreq_ack[1]) intreq[1] <= GND; // cleared by the CPU
end
end
// pio in command type
always @(posedge clk)
if (clk_en) begin
if (reset)
pio_in <= GND;
else if (drdy) // reset when processing of the current command ends
pio_in <= GND;
else if (busy && hdd_status_wr && hdd_data_out[3]) // set by SPI host
pio_in <= VCC;
end
// pio out command type
always @(posedge clk)
if (clk_en) begin
if (reset)
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[7]) // reset by SPI host when command processing completes
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[3]) // pio_in set by SPI host (during PACKET processing)
pio_out <= GND;
else if (busy && hdd_status_wr && hdd_data_out[2]) // set by SPI host
pio_out <= VCC;
end
// packet command state machine
always @(posedge clk)
if (clk_en) begin
if (reset)
packet_state <= PACKET_IDLE;
else if (drdy) // reset when processing of the current command ends
packet_state <= PACKET_IDLE;
else if (packet_state_change) // set by SPI host
packet_state <= packet_state == PACKET_IDLE ? PACKET_WAITCMD :
packet_state == PACKET_WAITCMD ? PACKET_PROCESSCMD : packet_state;
end
assign drq = (fifo_full & pio_in) | (~fifo_full & pio_out & (sector_count != 0 || packet_out)); // HDD data request status bit
// error status
always @(posedge clk)
if (clk_en) begin
if (reset)
error <= GND;
else if (sel_command) // reset by the CPU when command register is written
error <= GND;
else if (busy && hdd_status_wr && hdd_data_out[0]) // set by SPI host
error <= VCC;
end
assign hdd_cmd_req = bsy; // bsy is set when command register is written, tells the SPI host about new command
assign hdd_dat_req = (fifo_full & pio_out); // the FIFO is full so SPI host may read it
// FIFO in/out multiplexer
assign fifo_reset = reset | sel_command | packet_state_change | packet_in_last;
assign fifo_data_in = pio_in ? hdd_data_out : data_in;
assign fifo_rd = pio_out ? hdd_data_rd : sel_fifo & rd;
assign fifo_wr = pio_in ? hdd_data_wr : sel_fifo & hwr & lwr;
assign packet_in = packet_state == PACKET_PROCESSCMD && pio_in;
assign packet_out = packet_state == PACKET_WAITCMD || (packet_state == PACKET_PROCESSCMD && pio_out);
//sector data buffer (FIFO)
ide_fifo SECBUF1
(
.clk(clk),
.clk_en(clk_en),
.reset(fifo_reset),
.data_in(fifo_data_in),
.data_out(fifo_data_out),
.rd(fifo_rd),
.wr(fifo_wr),
.packet_in(packet_in),
.packet_out(packet_out),
.packet_count(packet_count),
.packet_in_last(packet_in_last),
.full(fifo_full),
.empty(fifo_empty),
.last_out(fifo_last_out),
.last_in(fifo_last_in)
);
// fifo is not ready for reading
assign nrdy = pio_in & sel_fifo & fifo_empty;
assign data_oe = (!dev[1] && hdd0_ena[dev[0]]) || (dev[1] && hdd1_ena[dev[0]]);
//data_out multiplexer
assign data_out = sel_fifo && rd ? fifo_data_out :
sel_status ? data_oe ? {status,8'h00} : 16'h00_00 :
sel_tfr && rd ? {tfr_out,8'h00} : 16'h00_00;
//===============================================================================================//
endmodule

86
common/mist/ide_fifo.v Normal file
View File

@ -0,0 +1,86 @@
module ide_fifo
(
input clk, // bus clock
input clk_en,
input reset, // reset
input [15:0] data_in, // data in
output reg [15:0] data_out, // data out
input rd, // read from fifo
input wr, // write to fifo
input packet_in,
input packet_out,
input [12:0] packet_count,
output packet_in_last, // last word is read in packet_in state
output full, // fifo is full
output empty, // fifo is empty
output last_out, // the last word of a sector is being read
output last_in // the last word of a sector is being written
);
// local signals and registers
reg [15:0] mem [4095:0]; // 16 bit wide fifo memory
reg [12:0] inptr; // fifo input pointer
reg [12:0] outptr; // fifo output pointer
wire empty_rd; // fifo empty flag (set immediately after reading the last word)
reg empty_wr; // fifo empty flag (set one clock after writting the empty fifo)
reg rd_old;
reg wr_old;
always @(posedge clk)
if (clk_en) begin
rd_old <= rd;
wr_old <= wr;
end
// main fifo memory (implemented using synchronous block ram)
always @(posedge clk)
if (clk_en) begin
if (wr)
mem[inptr[11:0]] <= data_in;
end
always @(posedge clk)
if (clk_en)
data_out <= mem[outptr[11:0]];
// fifo write pointer control
always @(posedge clk)
if (clk_en) begin
if (reset)
inptr <= 0;
else if (wr_old & ~wr)
inptr <= inptr + 1'd1;
end
// fifo read pointer control
always @(posedge clk)
if (clk_en) begin
if (reset)
outptr <= 0;
else if (rd_old & ~rd)
outptr <= outptr + 1'd1;
end
// the empty flag is set immediately after reading the last word from the fifo
assign empty_rd = inptr==outptr ? 1'b1 : 1'b0;
// after writting empty fifo the empty flag is delayed by one clock to handle ram write delay
always @(posedge clk)
if (clk_en)
empty_wr <= empty_rd;
assign empty = empty_rd | empty_wr;
// at least 512 bytes are in FIFO
// this signal is activated when 512th byte is written to the empty fifo
// then it's deactivated when 512th byte is read from the fifo (hysteresis)
// special handlig of packet commands
assign full = (inptr[12:8] != outptr[12:8] && !packet_in && !packet_out) ||
(packet_in && inptr == packet_count && inptr != outptr) ||
(packet_out && inptr == packet_count /*&& inptr != outptr*/);
assign packet_in_last = packet_in && inptr == packet_count && inptr == outptr && inptr != 0;
assign last_out = outptr[7:0] == 8'hFF ? 1'b1 : 1'b0;
assign last_in = inptr [7:0] == 8'hFF ? 1'b1 : 1'b0;
endmodule

View File

@ -7,4 +7,8 @@ set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) arcade_inputs.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) rgb2ypbpr.v]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) cofi.sv]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) sd_card.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) ide.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) ide_fifo.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) cdda_fifo.v]
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) dac.vhd]

View File

@ -48,6 +48,7 @@ port (
sd_wr : in std_logic_vector(SD_IMAGES-1 downto 0) := (others => '0');
sd_ack : out std_logic;
sd_ack_conf : out std_logic;
sd_ack_x : out std_logic_vector(SD_IMAGES-1 downto 0);
sd_conf : in std_logic := '0';
sd_sdhc : in std_logic := '1';
img_size : out std_logic_vector(63 downto 0);
@ -99,7 +100,7 @@ port (
SPI_DI : in std_logic;
scanlines : in std_logic_vector(1 downto 0);
ce_divider : in std_logic := '0';
ce_divider : in std_logic_vector(2 downto 0) := "000";
scandoubler_disable : in std_logic;
ypbpr : in std_logic;
rotate : in std_logic_vector(1 downto 0);

View File

@ -15,8 +15,9 @@ module mist_video
// scanlines (00-none 01-25% 10-50% 11-75%)
input [1:0] scanlines,
// non-scandoubled pixel clock divider 0 - clk_sys/4, 1 - clk_sys/2
input ce_divider,
// non-scandoubled pixel clock divider:
// 0 - clk_sys/4, 1 - clk_sys/2, 2 - clk_sys/3, 3 - clk_sys/4, etc
input [2:0] ce_divider,
// 0 = HVSync 31KHz, 1 = CSync 15KHz
input scandoubler_disable,
@ -59,7 +60,7 @@ wire [5:0] SD_B_O;
wire SD_HS_O;
wire SD_VS_O;
wire pixel_ena;
wire pixel_ena;
scandoubler #(SD_HCNT_WIDTH, COLOR_DEPTH) scandoubler
(

View File

@ -18,12 +18,25 @@
// TODO: Delay vsync one line
// AMR - generates and output a pixel clock with a reliable phase relationship with
// with the scandoubled hsync pulse. Allows the incoming data to be sampled more
// sparsely, reducing block RAM usage. ce_x1/x2 are replaced with a ce_divider
// which is the largest value the counter will reach before resetting - so 3'111 to
// divide clk_sys by 8, 3'011 to divide by 4, 3'101 to divide by six.
// Also now has a bypass mode, in which the incoming data will be scaled to the output
// width but otherwise unmodified. Simplifies the rest of the video chain.
module scandoubler
(
// system interface
input clk_sys,
input bypass,
input ce_divider,
// Pixelclock
input [2:0] ce_divider, // 0 - clk_sys/4, 1 - clk_sys/2, 2 - clk_sys/3, 3 - clk_sys/4, etc.
output pixel_ena,
// scanlines (00-none 01-25% 10-50% 11-75%)
@ -37,41 +50,16 @@ module scandoubler
input [COLOR_DEPTH-1:0] b_in,
// output interface
output reg hs_out,
output reg vs_out,
output reg [5:0] r_out,
output reg [5:0] g_out,
output reg [5:0] b_out
output hs_out,
output vs_out,
output [5:0] r_out,
output [5:0] g_out,
output [5:0] b_out
);
parameter HCNT_WIDTH = 9;
parameter COLOR_DEPTH = 6;
// pixel clock divider
reg [1:0] i_div;
reg ce_x1, ce_x2;
always @(posedge clk_sys) begin
reg last_hs_in;
last_hs_in <= hs_in;
if(last_hs_in & !hs_in) begin
i_div <= 2'b00;
end else begin
i_div <= i_div + 2'd1;
end
end
always @(*) begin
if (!ce_divider) begin
ce_x1 = (i_div == 2'b01);
ce_x2 = i_div[0];
end else begin
ce_x1 = i_div[0];
ce_x2 = 1'b1;
end
end
assign pixel_ena = bypass ? ce_x1 : ce_x2;
parameter HCNT_WIDTH = 9; // Resolution of scandoubler buffer
parameter COLOR_DEPTH = 6; // Bits per colour to be stored in the buffer
parameter HSCNT_WIDTH = 12; // Resolution of hsync counters
// --------------------- create output signals -----------------
// latch everything once more to make it glitch free and apply scanline effect
@ -80,74 +68,85 @@ reg [5:0] r;
reg [5:0] g;
reg [5:0] b;
wire [5:0] r_o;
wire [5:0] g_o;
wire [5:0] b_o;
reg hs_o;
reg vs_o;
wire [COLOR_DEPTH*3-1:0] sd_mux = bypass ? {r_in, g_in, b_in} : sd_out;
always @(*) begin
if (COLOR_DEPTH == 6) begin
b = sd_out[5:0];
g = sd_out[11:6];
r = sd_out[17:12];
b = sd_mux[5:0];
g = sd_mux[11:6];
r = sd_mux[17:12];
end else if (COLOR_DEPTH == 2) begin
b = {3{sd_out[1:0]}};
g = {3{sd_out[3:2]}};
r = {3{sd_out[5:4]}};
b = {3{sd_mux[1:0]}};
g = {3{sd_mux[3:2]}};
r = {3{sd_mux[5:4]}};
end else if (COLOR_DEPTH == 1) begin
b = {6{sd_out[0]}};
g = {6{sd_out[1]}};
r = {6{sd_out[2]}};
b = {6{sd_mux[0]}};
g = {6{sd_mux[1]}};
r = {6{sd_mux[2]}};
end else begin
b = { sd_out[COLOR_DEPTH-1:0], sd_out[COLOR_DEPTH-1 -:(6-COLOR_DEPTH)] };
g = { sd_out[COLOR_DEPTH*2-1:COLOR_DEPTH], sd_out[COLOR_DEPTH*2-1 -:(6-COLOR_DEPTH)] };
r = { sd_out[COLOR_DEPTH*3-1:COLOR_DEPTH*2], sd_out[COLOR_DEPTH*3-1 -:(6-COLOR_DEPTH)] };
b = { sd_mux[COLOR_DEPTH-1:0], sd_mux[COLOR_DEPTH-1 -:(6-COLOR_DEPTH)] };
g = { sd_mux[COLOR_DEPTH*2-1:COLOR_DEPTH], sd_mux[COLOR_DEPTH*2-1 -:(6-COLOR_DEPTH)] };
r = { sd_mux[COLOR_DEPTH*3-1:COLOR_DEPTH*2], sd_mux[COLOR_DEPTH*3-1 -:(6-COLOR_DEPTH)] };
end
end
reg [12:0] r_mul;
reg [12:0] g_mul;
reg [12:0] b_mul;
wire scanline_bypass = (!scanline) | (!(|scanlines)) | bypass;
// More subtle variant of the scanlines effect.
// 0 00 -> 1000000 0x40 - bypass / inert mode
// 1 01 -> 0111010 0x3a - 25%
// 2 10 -> 0101110 0x2e - 50%
// 3 11 -> 0011010 0x1a - 75%
wire [6:0] scanline_coeff = scanline_bypass ?
7'b1000000 : {~(&scanlines),scanlines[0],1'b1,~scanlines[0],2'b10};
always @(posedge clk_sys) begin
if(bypass) begin
r_out <= r;
g_out <= g;
b_out <= b;
hs_out <= hs_sd;
vs_out <= vs_sd;
end else if(ce_x2) begin
hs_out <= hs_sd;
vs_out <= vs_sd;
if(ce_x2) begin
hs_o <= hs_sd;
vs_o <= vs_in;
// reset scanlines at every new screen
if(vs_out != vs_in) scanline <= 0;
if(vs_o != vs_in) scanline <= 0;
// toggle scanlines at begin of every hsync
if(hs_out && !hs_sd) scanline <= !scanline;
if(hs_o && !hs_sd) scanline <= !scanline;
// if no scanlines or not a scanline
if(!scanline || !scanlines) begin
r_out <= r;
g_out <= g;
b_out <= b;
end else begin
case(scanlines)
1: begin // reduce 25% = 1/2 + 1/4
r_out <= {1'b0, r[5:1]} + {2'b00, r[5:2] };
g_out <= {1'b0, g[5:1]} + {2'b00, g[5:2] };
b_out <= {1'b0, b[5:1]} + {2'b00, b[5:2] };
end
2: begin // reduce 50% = 1/2
r_out <= {1'b0, r[5:1]};
g_out <= {1'b0, g[5:1]};
b_out <= {1'b0, b[5:1]};
end
3: begin // reduce 75% = 1/4
r_out <= {2'b00, r[5:2]};
g_out <= {2'b00, g[5:2]};
b_out <= {2'b00, b[5:2]};
end
endcase
end
r_mul<=r*scanline_coeff;
g_mul<=g*scanline_coeff;
b_mul<=b*scanline_coeff;
end
end
assign r_o = r_mul[11:6];
assign g_o = g_mul[11:6];
assign b_o = b_mul[11:6];
// Output multiplexing
assign r_out = bypass ? r : r_o;
assign g_out = bypass ? g : g_o;
assign b_out = bypass ? b : b_o;
assign hs_out = bypass ? hs_in : hs_o;
assign vs_out = bypass ? vs_in : vs_o;
assign pixel_ena = bypass ? ce_x1 : ce_x2;
// scan doubler output register
wire [COLOR_DEPTH*3-1:0] sd_out = bypass ? sd_bypass_out : sd_buffer_out;
reg [COLOR_DEPTH*3-1:0] sd_out;
// ==================================================================
// ======================== the line buffers ========================
@ -160,70 +159,104 @@ wire [COLOR_DEPTH*3-1:0] sd_out = bypass ? sd_bypass_out : sd_buffer_out;
reg line_toggle;
// total hsync time (in 16MHz cycles), hs_total reaches 1024
reg [HCNT_WIDTH-1:0] hs_max;
reg [HCNT_WIDTH-1:0] hs_rise;
reg [HCNT_WIDTH-1:0] hcnt;
reg [HSCNT_WIDTH:0] hs_max;
reg [HSCNT_WIDTH:0] hs_rise;
reg [HSCNT_WIDTH:0] synccnt;
// Input pixel clock, aligned with input sync:
wire[2:0] ce_divider_adj = |ce_divider ? ce_divider : 3'd3; // 0 = clk/4 for compatiblity
reg [2:0] ce_divider_in;
reg [2:0] ce_divider_out;
reg [2:0] i_div;
wire ce_x1 = (i_div == ce_divider_in);
always @(posedge clk_sys) begin
reg hsD, vsD;
// Pixel logic on x1 clkena
if(ce_x1) begin
hsD <= hs_in;
// falling edge of hsync indicates start of line
if(hsD && !hs_in) begin
hs_max <= hcnt;
hcnt <= 0;
end else begin
hcnt <= hcnt + 1'd1;
end
// save position of rising edge
if(!hsD && hs_in) hs_rise <= hcnt;
vsD <= vs_in;
if(vsD != vs_in) line_toggle <= 0;
// begin of incoming hsync
if(hsD && !hs_in) line_toggle <= !line_toggle;
hcnt <= hcnt + 1'd1;
sd_buffer[{line_toggle, hcnt}] <= {r_in, g_in, b_in};
end
// Generate pixel clock
i_div <= i_div + 1'd1;
if (i_div==ce_divider_adj) i_div <= 3'b000;
synccnt <= synccnt + 1'd1;
hsD <= hs_in;
if(hsD && !hs_in) begin
// At hsync latch the ce_divider counter limit for the input clock
// and pass the previous input clock limit to the output stage.
// This should give correct output if the pixel clock changes mid-screen.
ce_divider_out <= ce_divider_in;
ce_divider_in <= ce_divider_adj;
hs_max <= {1'b0,synccnt[HSCNT_WIDTH:1]};
hcnt <= 0;
synccnt <= 0;
i_div <= 3'b000;
end
// save position of rising edge
if(!hsD && hs_in) hs_rise <= {1'b0,synccnt[HSCNT_WIDTH:1]};
// begin of incoming hsync
if(hsD && !hs_in) line_toggle <= !line_toggle;
vsD <= vs_in;
if(vsD != vs_in) line_toggle <= 0;
end
// ==================================================================
// ==================== output timing generation ====================
// ==================================================================
reg [COLOR_DEPTH*3-1:0] sd_buffer_out, sd_bypass_out;
reg [HCNT_WIDTH-1:0] sd_hcnt;
reg hs_sd, vs_sd;
reg [HSCNT_WIDTH:0] sd_synccnt;
reg [HCNT_WIDTH-1:0] sd_hcnt;
reg hs_sd;
// Output pixel clock, aligned with output sync:
reg [2:0] sd_i_div;
wire ce_x2 = (sd_i_div == ce_divider_out) | (sd_i_div == {1'b0,ce_divider_out[2:1]});
// timing generation runs 32 MHz (twice the input signal analysis speed)
always @(posedge clk_sys) begin
reg hsD;
// Output logic on x2 clkena
if(ce_x2) begin
hsD <= hs_in;
// output counter synchronous to input and at twice the rate
sd_hcnt <= sd_hcnt + 1'd1;
if(hsD && !hs_in) sd_hcnt <= hs_max;
if(sd_hcnt == hs_max) sd_hcnt <= 0;
// replicate horizontal sync at twice the speed
if(sd_hcnt == hs_max) hs_sd <= 0;
if(sd_hcnt == hs_rise) hs_sd <= 1;
// read data from line sd_buffer
sd_buffer_out <= sd_buffer[{~line_toggle, sd_hcnt}];
vs_sd <= vs_in;
sd_out <= sd_buffer[{~line_toggle, sd_hcnt}];
end
if(bypass) begin
sd_bypass_out <= {r_in, g_in, b_in};
hs_sd <= hs_in;
vs_sd <= vs_in;
// Framing logic on sysclk
sd_synccnt <= sd_synccnt + 1'd1;
hsD <= hs_in;
if(hsD && !hs_in) sd_synccnt <= hs_max;
if(sd_synccnt == hs_max) begin
sd_synccnt <= 0;
sd_hcnt <= 0;
end
sd_i_div <= sd_i_div + 1'd1;
if (sd_i_div==ce_divider_adj) sd_i_div <= 3'b000;
// replicate horizontal sync at twice the speed
if(sd_synccnt == 0) begin
hs_sd <= 0;
sd_i_div <= 3'b000;
end
if(sd_synccnt == hs_rise) hs_sd <= 1;
end
endmodule

View File

@ -503,6 +503,9 @@ always@(posedge clk_sys) begin
reply_len <= 4'd4;
end
// CMD59: CRC_ON_OFF
8'h7b: reply <= 0; // ok
endcase
end
end

View File

@ -2,7 +2,7 @@
// user_io.v
//
// user_io for the MiST board
// http://code.google.com/p/mist-board/
// https://github.com/mist-devel
//
// Copyright (c) 2014 Till Harbaum <till@harbaum.org>
//
@ -57,14 +57,15 @@ module user_io (
input [31:0] sd_lba,
input [SD_IMAGES-1:0] sd_rd,
input [SD_IMAGES-1:0] sd_wr,
output reg sd_ack,
output reg sd_ack_conf,
output reg sd_ack = 0, // ack any transfer
output reg sd_ack_conf = 0,
output reg [SD_IMAGES-1:0] sd_ack_x = 0, // ack specific transfer
input sd_conf,
input sd_sdhc,
output reg [7:0] sd_dout, // valid on rising edge of sd_dout_strobe
output reg sd_dout_strobe,
output reg sd_dout_strobe = 0,
input [7:0] sd_din,
output reg sd_din_strobe,
output reg sd_din_strobe = 0,
output reg [8:0] sd_buff_addr,
output reg [SD_IMAGES-1:0] img_mounted, // rising edge if a new image is mounted
@ -72,11 +73,11 @@ module user_io (
// ps2 keyboard/mouse emulation
output ps2_kbd_clk,
output reg ps2_kbd_data,
output ps2_kbd_data,
input ps2_kbd_clk_i,
input ps2_kbd_data_i,
output ps2_mouse_clk,
output reg ps2_mouse_data,
output ps2_mouse_data,
input ps2_mouse_clk_i,
input ps2_mouse_data_i,
@ -86,6 +87,9 @@ module user_io (
output reg [7:0] key_code, // key scan code
output reg key_strobe, // key data valid
input [7:0] kbd_out_data, // for Archie
input kbd_out_strobe,
// mouse data
output reg [8:0] mouse_x,
output reg [8:0] mouse_y,
@ -105,8 +109,10 @@ parameter ROM_DIRECT_UPLOAD=0; // direct upload used for file uploads from the A
parameter SD_IMAGES=2; // number of block-access images (max. 4 supported in current firmware)
parameter PS2BIDIR=0; // bi-directional PS2 interface
parameter FEATURES=0; // requested features from the firmware
parameter ARCHIE=0;
localparam W = $clog2(SD_IMAGES);
localparam PS2_FIFO_BITS = 4;
reg [6:0] sbuf;
reg [7:0] cmd;
@ -123,9 +129,8 @@ assign no_csync = but_sw[6];
assign conf_addr = byte_cnt;
// this variant of user_io is for 8 bit cores (type == a4) only
// bit 4 indicates ROM direct upload capability
wire [7:0] core_type = ROM_DIRECT_UPLOAD ? 8'hb4 : 8'ha4;
wire [7:0] core_type = ARCHIE ? 8'ha6 : ROM_DIRECT_UPLOAD ? 8'hb4 : 8'ha4;
reg [W:0] drive_sel;
always begin
@ -140,9 +145,6 @@ wire [7:0] sd_cmd = { 4'h6, sd_conf, sd_sdhc, sd_wr[drive_sel], sd_rd[drive_sel]
wire spi_sck = SPI_CLK;
// ---------------- PS2 ---------------------
// 16 byte fifos to store ps2 bytes
localparam PS2_FIFO_BITS = 4;
reg ps2_clk;
always @(posedge clk_sys) begin
integer cnt;
@ -154,237 +156,44 @@ always @(posedge clk_sys) begin
end
// keyboard
reg [7:0] ps2_kbd_fifo [(2**PS2_FIFO_BITS)-1:0];
reg [PS2_FIFO_BITS-1:0] ps2_kbd_wptr;
reg [PS2_FIFO_BITS-1:0] ps2_kbd_rptr;
reg ps2_kbd_tx_strobe;
wire [7:0] ps2_kbd_rx_byte ;
wire ps2_kbd_rx_strobe;
wire ps2_kbd_fifo_ok;
// ps2 transmitter state machine
reg [3:0] ps2_kbd_tx_state;
reg [7:0] ps2_kbd_tx_byte;
reg ps2_kbd_parity;
// ps2 receiver state machine
reg [3:0] ps2_kbd_rx_state = 0;
reg [1:0] ps2_kbd_rx_start = 0;
reg [7:0] ps2_kbd_rx_byte = 0;
reg ps2_kbd_rx_strobe = 0;
assign ps2_kbd_clk = ps2_clk || (ps2_kbd_tx_state == 0 && ps2_kbd_rx_state == 0);
// ps2 transmitter/receiver
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
// Sends a command to the IO controller if bidirectional mode is enabled.
always@(posedge clk_sys) begin : ps2_kbd
reg ps2_clkD;
reg ps2_clk_iD, ps2_dat_iD;
reg ps2_kbd_r_inc;
// send data
ps2_clkD <= ps2_clk;
if (~ps2_clkD & ps2_clk) begin
ps2_kbd_r_inc <= 1'b0;
if(ps2_kbd_r_inc)
ps2_kbd_rptr <= ps2_kbd_rptr + 1'd1;
// transmitter is idle?
if(ps2_kbd_tx_state == 0) begin
ps2_kbd_data <= 1;
// data in fifo present?
if(ps2_kbd_wptr != ps2_kbd_rptr && (ps2_kbd_clk_i | !PS2BIDIR)) begin
// load tx register from fifo
ps2_kbd_tx_byte <= ps2_kbd_fifo[ps2_kbd_rptr];
ps2_kbd_r_inc <= 1'b1;
// reset parity
ps2_kbd_parity <= 1'b1;
// start transmitter
ps2_kbd_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_kbd_tx_state >= 1)&&(ps2_kbd_tx_state < 9)) begin
ps2_kbd_data <= ps2_kbd_tx_byte[0]; // data bits
ps2_kbd_tx_byte[6:0] <= ps2_kbd_tx_byte[7:1]; // shift down
if(ps2_kbd_tx_byte[0])
ps2_kbd_parity <= !ps2_kbd_parity;
end
// transmission of parity
if(ps2_kbd_tx_state == 9)
ps2_kbd_data <= ps2_kbd_parity;
// transmission of stop bit
if(ps2_kbd_tx_state == 10)
ps2_kbd_data <= 1'b1; // stop bit is 1
// advance state machine
if(ps2_kbd_tx_state < 11)
ps2_kbd_tx_state <= ps2_kbd_tx_state + 4'd1;
else
ps2_kbd_tx_state <= 4'd0;
end
end
if (PS2BIDIR) begin
ps2_clk_iD <= ps2_kbd_clk_i;
ps2_dat_iD <= ps2_kbd_data_i;
// receive command
case (ps2_kbd_rx_start)
2'd0:
// first: host pulls down the clock line
if (ps2_clk_iD & ~ps2_kbd_clk_i) ps2_kbd_rx_start <= 1;
2'd1:
// second: host pulls down the data line, while releasing the clock
if (ps2_dat_iD && !ps2_kbd_data_i) ps2_kbd_rx_start <= 2'd2;
// if it releases the clock without pulling down the data line: goto 0
else if (ps2_kbd_clk_i) ps2_kbd_rx_start <= 0;
2'd2:
if (ps2_clkD && ~ps2_clk) begin
ps2_kbd_rx_state <= 4'd1;
ps2_kbd_rx_start <= 0;
end
default: ;
endcase
// host data is valid after the rising edge of the clock
if(ps2_kbd_rx_state != 0 && ~ps2_clkD && ps2_clk) begin
ps2_kbd_rx_state <= ps2_kbd_rx_state + 1'd1;
if (ps2_kbd_rx_state == 9) ;// parity
else if (ps2_kbd_rx_state == 10) begin
ps2_kbd_data <= 0; // ack the received byte
end else if (ps2_kbd_rx_state == 11) begin
ps2_kbd_rx_state <= 0;
ps2_kbd_rx_strobe <= ~ps2_kbd_rx_strobe;
end else begin
ps2_kbd_rx_byte <= {ps2_kbd_data_i, ps2_kbd_rx_byte[7:1]};
end
end
end
end
user_io_ps2 #(.PS2_BIDIR(PS2BIDIR), .PS2_FIFO_BITS(4)) ps2_kbd (
.clk_sys ( clk_sys ),
.ps2_clk ( ps2_clk ),
.ps2_clk_i ( ps2_kbd_clk_i ),
.ps2_clk_o ( ps2_kbd_clk ),
.ps2_data_i ( ps2_kbd_data_i ),
.ps2_data_o ( ps2_kbd_data ),
.ps2_tx_strobe ( ps2_kbd_tx_strobe ), // from IO controller
.ps2_tx_byte ( spi_byte_in ),
.ps2_rx_strobe ( ps2_kbd_rx_strobe ), // to IO controller
.ps2_rx_byte ( ps2_kbd_rx_byte ),
.ps2_fifo_ready( ps2_kbd_fifo_ok )
);
// mouse
reg [7:0] ps2_mouse_fifo [(2**PS2_FIFO_BITS)-1:0];
reg [PS2_FIFO_BITS-1:0] ps2_mouse_wptr;
reg [PS2_FIFO_BITS-1:0] ps2_mouse_rptr;
reg ps2_mouse_tx_strobe;
wire [7:0] ps2_mouse_rx_byte ;
wire ps2_mouse_rx_strobe;
wire ps2_mouse_fifo_ok;
// ps2 transmitter state machine
reg [3:0] ps2_mouse_tx_state;
reg [7:0] ps2_mouse_tx_byte;
reg ps2_mouse_parity;
// ps2 receiver state machine
reg [3:0] ps2_mouse_rx_state = 0;
reg [1:0] ps2_mouse_rx_start = 0;
reg [7:0] ps2_mouse_rx_byte = 0;
reg ps2_mouse_rx_strobe = 0;
assign ps2_mouse_clk = ps2_clk || (ps2_mouse_tx_state == 0 && ps2_mouse_rx_state == 0);
// ps2 transmitter/receiver
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
// Sends a command to the IO controller if bidirectional mode is enabled.
always@(posedge clk_sys) begin : ps2_mouse
reg ps2_clkD;
reg ps2_clk_iD, ps2_dat_iD;
reg ps2_mouse_r_inc;
ps2_clkD <= ps2_clk;
if (~ps2_clkD & ps2_clk) begin
ps2_mouse_r_inc <= 1'b0;
if(ps2_mouse_r_inc)
ps2_mouse_rptr <= ps2_mouse_rptr + 1'd1;
// transmitter is idle?
if(ps2_mouse_tx_state == 0) begin
ps2_mouse_data <= 1;
// data in fifo present?
if(ps2_mouse_wptr != ps2_mouse_rptr && (ps2_mouse_clk_i | !PS2BIDIR)) begin
// load tx register from fifo
ps2_mouse_tx_byte <= ps2_mouse_fifo[ps2_mouse_rptr];
ps2_mouse_r_inc <= 1'b1;
// reset parity
ps2_mouse_parity <= 1'b1;
// start transmitter
ps2_mouse_tx_state <= 4'd1;
// put start bit on data line
ps2_mouse_data <= 1'b0; // start bit is 0
end
end else begin
// transmission of 8 data bits
if((ps2_mouse_tx_state >= 1)&&(ps2_mouse_tx_state < 9)) begin
ps2_mouse_data <= ps2_mouse_tx_byte[0]; // data bits
ps2_mouse_tx_byte[6:0] <= ps2_mouse_tx_byte[7:1]; // shift down
if(ps2_mouse_tx_byte[0])
ps2_mouse_parity <= !ps2_mouse_parity;
end
// transmission of parity
if(ps2_mouse_tx_state == 9)
ps2_mouse_data <= ps2_mouse_parity;
// transmission of stop bit
if(ps2_mouse_tx_state == 10)
ps2_mouse_data <= 1'b1; // stop bit is 1
// advance state machine
if(ps2_mouse_tx_state < 11)
ps2_mouse_tx_state <= ps2_mouse_tx_state + 4'd1;
else
ps2_mouse_tx_state <= 4'd0;
end
end
if (PS2BIDIR) begin
ps2_clk_iD <= ps2_mouse_clk_i;
ps2_dat_iD <= ps2_mouse_data_i;
// receive command
case (ps2_mouse_rx_start)
2'd0:
// first: host pulls down the clock line
if (ps2_clk_iD & ~ps2_mouse_clk_i) ps2_mouse_rx_start <= 1;
2'd1:
// second: host pulls down the data line, while releasing the clock
if (ps2_dat_iD && !ps2_mouse_data_i) ps2_mouse_rx_start <= 2'd2;
// if it releases the clock without pulling down the data line: goto 0
else if (ps2_mouse_clk_i) ps2_mouse_rx_start <= 0;
2'd2:
if (ps2_clkD && ~ps2_clk) begin
ps2_mouse_rx_state <= 4'd1;
ps2_mouse_rx_start <= 0;
end
default: ;
endcase
// host data is valid after the rising edge of the clock
if(ps2_mouse_rx_state != 0 && ~ps2_clkD && ps2_clk) begin
ps2_mouse_rx_state <= ps2_mouse_rx_state + 1'd1;
if (ps2_mouse_rx_state == 9) ;// parity
else if (ps2_mouse_rx_state == 10) begin
ps2_mouse_data <= 0; // ack the received byte
end else if (ps2_mouse_rx_state == 11) begin
ps2_mouse_rx_state <= 0;
ps2_mouse_rx_strobe <= ~ps2_mouse_rx_strobe;
end else begin
ps2_mouse_rx_byte <= {ps2_mouse_data_i, ps2_mouse_rx_byte[7:1]};
end
end
end
end
user_io_ps2 #(.PS2_BIDIR(PS2BIDIR), .PS2_FIFO_BITS(3)) ps2_mouse (
.clk_sys ( clk_sys ),
.ps2_clk ( ps2_clk ),
.ps2_clk_i ( ps2_mouse_clk_i ),
.ps2_clk_o ( ps2_mouse_clk ),
.ps2_data_i ( ps2_mouse_data_i ),
.ps2_data_o ( ps2_mouse_data ),
.ps2_tx_strobe ( ps2_mouse_tx_strobe ), // from IO controller
.ps2_tx_byte ( spi_byte_in ),
.ps2_rx_strobe ( ps2_mouse_rx_strobe ), // to IO controller
.ps2_rx_byte ( ps2_mouse_rx_byte ),
.ps2_fifo_ready( ps2_mouse_fifo_ok )
);
// fifo to receive serial data from core to be forwarded to io controller
@ -446,6 +255,23 @@ always@(negedge spi_sck or posedge SPI_SS_IO) begin : spi_byteout
end
end
generate if (ARCHIE) begin
reg [7:0] kbd_out_status;
reg [7:0] kbd_out_data_r;
reg kbd_out_data_available = 0;
always@(negedge spi_sck or posedge SPI_SS_IO) begin : archie_kbd_out
if(SPI_SS_IO == 1) begin
kbd_out_data_r <= 0;
kbd_out_status <= 0;
end else begin
kbd_out_status <= { 4'ha, 3'b000, kbd_out_data_available };
kbd_out_data_r <= kbd_out_data;
end
end
end
endgenerate
always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_transmitter
reg [31:0] sd_lba_r;
reg [W:0] drive_sel_r;
@ -461,6 +287,11 @@ always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_transmitter
spi_byte_out <= 0;
case({(!byte_cnt) ? {sbuf, SPI_MOSI} : cmd})
8'h04: if (ARCHIE) begin
if(byte_cnt == 0) spi_byte_out <= kbd_out_status;
else spi_byte_out <= kbd_out_data_r;
end
// PS2 keyboard command
8'h0e: if (byte_cnt == 0) begin
ps2_kbd_rx_strobeD <= ps2_kbd_rx_strobe;
@ -539,12 +370,14 @@ always @(posedge clk_sys) begin : cmd_block
reg spi_receiver_strobeD;
reg spi_transfer_endD;
reg [7:0] acmd;
reg [7:0] abyte_cnt; // counts bytes
reg [3:0] abyte_cnt; // counts bytes
reg [7:0] mouse_flags_r;
reg [7:0] mouse_x_r;
reg [7:0] mouse_y_r;
reg mouse_fifo_ok;
reg kbd_fifo_ok;
reg key_pressed_r;
reg key_extended_r;
@ -556,17 +389,46 @@ always @(posedge clk_sys) begin : cmd_block
key_strobe <= 0;
mouse_strobe <= 0;
ps2_kbd_tx_strobe <= 0;
ps2_mouse_tx_strobe <= 0;
if(ARCHIE) begin
if (kbd_out_strobe) kbd_out_data_available <= 1;
key_pressed <= 0;
key_extended <= 0;
mouse_x <= 0;
mouse_y <= 0;
mouse_z <= 0;
mouse_flags <= 0;
mouse_idx <= 0;
end
if (spi_transfer_end) begin
abyte_cnt <= 8'd0;
abyte_cnt <= 0;
mouse_fifo_ok <= 0;
kbd_fifo_ok <= 0;
end else if (spi_receiver_strobeD ^ spi_receiver_strobe) begin
if(~&abyte_cnt)
abyte_cnt <= abyte_cnt + 8'd1;
abyte_cnt <= abyte_cnt + 1'd1;
if(abyte_cnt == 0) begin
acmd <= spi_byte_in;
if (spi_byte_in == 8'h70 || spi_byte_in == 8'h71)
// accept the incoming mouse data only if there's place for the full packet
mouse_fifo_ok <= ps2_mouse_fifo_ok;
if (spi_byte_in == 8'h05)
// accept the incoming keyboard data only if there's place for the full packet
kbd_fifo_ok <= ps2_kbd_fifo_ok;
end else begin
if (ARCHIE) begin
if(acmd == 8'h04) kbd_out_data_available <= 0;
if(acmd == 8'h05) begin
key_strobe <= 1;
key_code <= spi_byte_in;
end
end
case(acmd)
// buttons and switches
8'h01: but_sw <= spi_byte_in;
@ -575,11 +437,10 @@ always @(posedge clk_sys) begin : cmd_block
8'h62: if (abyte_cnt < 5) joystick_2[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h63: if (abyte_cnt < 5) joystick_3[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h64: if (abyte_cnt < 5) joystick_4[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h70,8'h71: begin
8'h70,8'h71: if (!ARCHIE) begin
// store incoming ps2 mouse bytes
if (abyte_cnt < 4) begin
ps2_mouse_fifo[ps2_mouse_wptr] <= spi_byte_in;
ps2_mouse_wptr <= ps2_mouse_wptr + 1'd1;
if (abyte_cnt < 4 && mouse_fifo_ok) begin
ps2_mouse_tx_strobe <= 1;
end
if (abyte_cnt == 1) mouse_flags_r <= spi_byte_in;
@ -595,10 +456,9 @@ always @(posedge clk_sys) begin : cmd_block
mouse_strobe <= 1;
end
end
8'h05: begin
// store incoming ps2 keyboard bytes
ps2_kbd_fifo[ps2_kbd_wptr] <= spi_byte_in;
ps2_kbd_wptr <= ps2_kbd_wptr + 1'd1;
8'h05: if (!ARCHIE) begin
// store incoming ps2 keyboard bytes
if (kbd_fifo_ok) ps2_kbd_tx_strobe <= 1;
if (abyte_cnt == 1) begin
key_extended_r <= 0;
key_pressed_r <= 1;
@ -698,6 +558,7 @@ always @(posedge clk_sd) begin : sd_block
sd_ack <= 1'b0;
sd_ack_conf <= 1'b0;
sd_buff_addr <= 0;
if (acmd == 8'h17 || acmd == 8'h18) sd_ack_x <= 0;
end else if (spi_receiver_strobeD ^ spi_receiver_strobe) begin
if(~&abyte_cnt)
@ -740,9 +601,160 @@ always @(posedge clk_sd) begin : sd_block
// send image info
8'h1d: if(abyte_cnt<9) img_size[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
// data transfer ack
8'h23: sd_ack_x <= 1'b1 << spi_byte_in;
endcase
end
end
end
endmodule
module user_io_ps2 (
input clk_sys,
input ps2_clk,
input ps2_clk_i,
output ps2_clk_o,
input ps2_data_i,
output reg ps2_data_o = 1,
input ps2_tx_strobe, // from IO controller
input [7:0] ps2_tx_byte,
output reg ps2_rx_strobe = 0, // to IO controller
output reg [7:0] ps2_rx_byte = 0,
output ps2_fifo_ready
);
parameter PS2_FIFO_BITS = 4;
parameter PS2_BIDIR = 0;
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;
wire [PS2_FIFO_BITS:0] ps2_used = ps2_wptr >= ps2_rptr ?
ps2_wptr - ps2_rptr :
ps2_wptr - ps2_rptr + (2'd2**PS2_FIFO_BITS);
wire [PS2_FIFO_BITS:0] ps2_free = (2'd2**PS2_FIFO_BITS) - ps2_used;
assign ps2_fifo_ready = ps2_free[PS2_FIFO_BITS:2] != 0; // ps2_free > 3
// ps2 transmitter state machine
reg [3:0] ps2_tx_state;
reg [7:0] ps2_tx_shift_reg;
reg ps2_parity;
// ps2 receiver state machine
reg [3:0] ps2_rx_state = 0;
reg [1:0] ps2_rx_start = 0;
assign ps2_clk_o = ps2_clk || (ps2_tx_state == 0 && ps2_rx_state == 0);
always@(posedge clk_sys) begin : ps2_fifo_wr
if (ps2_tx_strobe) begin
ps2_fifo[ps2_wptr] <= ps2_tx_byte;
ps2_wptr <= ps2_wptr + 1'd1;
end
end
// ps2 transmitter/receiver
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
// Sends a command to the IO controller if bidirectional mode is enabled.
always@(posedge clk_sys) begin : ps2_txrx
reg ps2_clkD;
reg ps2_clk_iD, ps2_dat_iD;
reg ps2_r_inc;
ps2_clkD <= ps2_clk;
if (~ps2_clkD & ps2_clk) begin
ps2_r_inc <= 1'b0;
if(ps2_r_inc)
ps2_rptr <= ps2_rptr + 1'd1;
// transmitter is idle?
if(ps2_tx_state == 0) begin
ps2_data_o <= 1;
// data in fifo present?
if(ps2_wptr != ps2_rptr && (ps2_clk_i | !PS2_BIDIR)) begin
// load tx register from fifo
ps2_tx_shift_reg <= 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_data_o <= 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_data_o <= ps2_tx_shift_reg[0]; // data bits
ps2_tx_shift_reg[6:0] <= ps2_tx_shift_reg[7:1]; // shift down
if(ps2_tx_shift_reg[0])
ps2_parity <= !ps2_parity;
end
// transmission of parity
if(ps2_tx_state == 9)
ps2_data_o <= ps2_parity;
// transmission of stop bit
if(ps2_tx_state == 10)
ps2_data_o <= 1'b1; // stop bit is 1
// advance state machine
if(ps2_tx_state == 11)
ps2_tx_state <= 4'd0;
else
ps2_tx_state <= ps2_tx_state + 4'd1;
end
end
if (PS2_BIDIR) begin
ps2_clk_iD <= ps2_clk_i;
ps2_dat_iD <= ps2_data_i;
// receive command
case (ps2_rx_start)
2'd0:
// first: host pulls down the clock line
if (ps2_clk_iD & ~ps2_clk_i) ps2_rx_start <= 1;
2'd1:
// second: host pulls down the data line, while releasing the clock
if (ps2_dat_iD && !ps2_data_i) ps2_rx_start <= 2'd2;
// if it releases the clock without pulling down the data line: goto 0
else if (ps2_clk_i) ps2_rx_start <= 0;
2'd2:
if (ps2_clkD && ~ps2_clk) begin
ps2_rx_state <= 4'd1;
ps2_rx_start <= 0;
end
default: ;
endcase
// host data is valid after the rising edge of the clock
if(ps2_rx_state != 0 && ~ps2_clkD && ps2_clk) begin
ps2_rx_state <= ps2_rx_state + 1'd1;
if (ps2_rx_state == 9) ;// parity
else if (ps2_rx_state == 10) begin
ps2_data_o <= 0; // ack the received byte
end else if (ps2_rx_state == 11) begin
ps2_rx_state <= 0;
ps2_rx_strobe <= ~ps2_rx_strobe;
end else begin
ps2_rx_byte <= {ps2_data_i, ps2_rx_byte[7:1]};
end
end
end else begin
ps2_rx_byte <= 0;
ps2_rx_strobe <= 0;
end
end
endmodule