diff --git a/common/mist/README.md b/common/mist/README.md
index 588d54cd..99157a20 100644
--- a/common/mist/README.md
+++ b/common/mist/README.md
@@ -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.
diff --git a/common/mist/cdda_fifo.v b/common/mist/cdda_fifo.v
new file mode 100644
index 00000000..d402930f
--- /dev/null
+++ b/common/mist/cdda_fifo.v
@@ -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 .
+//
+///////////////////////////////////////////////////////////////////////
+
+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
diff --git a/common/mist/data_io.v b/common/mist/data_io.v
index 7e14f129..6862f1f4 100644
--- a/common/mist/data_io.v
+++ b/common/mist/data_io.v
@@ -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
//
@@ -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
diff --git a/common/mist/ide.v b/common/mist/ide.v
new file mode 100644
index 00000000..393699c4
--- /dev/null
+++ b/common/mist/ide.v
@@ -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 .
+//
+//
+//
+// -- 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
diff --git a/common/mist/ide_fifo.v b/common/mist/ide_fifo.v
new file mode 100644
index 00000000..24221efe
--- /dev/null
+++ b/common/mist/ide_fifo.v
@@ -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
diff --git a/common/mist/mist.qip b/common/mist/mist.qip
index de360210..923b5289 100644
--- a/common/mist/mist.qip
+++ b/common/mist/mist.qip
@@ -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]
diff --git a/common/mist/mist.vhd b/common/mist/mist.vhd
index d37c947c..2255c8b7 100644
--- a/common/mist/mist.vhd
+++ b/common/mist/mist.vhd
@@ -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);
diff --git a/common/mist/mist_video.v b/common/mist/mist_video.v
index 070ab50b..712b1dff 100644
--- a/common/mist/mist_video.v
+++ b/common/mist/mist_video.v
@@ -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
(
diff --git a/common/mist/scandoubler.v b/common/mist/scandoubler.v
index c9f0d684..8c54632b 100644
--- a/common/mist/scandoubler.v
+++ b/common/mist/scandoubler.v
@@ -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
diff --git a/common/mist/sd_card.v b/common/mist/sd_card.v
index 0d1ff58d..88fcbb30 100644
--- a/common/mist/sd_card.v
+++ b/common/mist/sd_card.v
@@ -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
diff --git a/common/mist/user_io.v b/common/mist/user_io.v
index 3d283918..05e114dd 100644
--- a/common/mist/user_io.v
+++ b/common/mist/user_io.v
@@ -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
//
@@ -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