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:
parent
05c5117d3f
commit
fe94350806
@ -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
107
common/mist/cdda_fifo.v
Normal 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
|
||||
@ -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
405
common/mist/ide.v
Normal 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
86
common/mist/ide_fifo.v
Normal 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
|
||||
@ -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]
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user