From c29cdda3a78dbc3bb145e057e886ca912cb428c7 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 13 Jul 2024 20:45:08 +0200 Subject: [PATCH] Update MiST Modules --- common/mist/.gitignore | 1 + common/mist/cofi.sv | 29 ++-- common/mist/data_io.v | 172 +++++++++--------- common/mist/i2c_master.v | 111 ++++++++++++ common/mist/i2s.v | 72 ++++++++ common/mist/mist.qip | 32 ++-- common/mist/mist.vhd | 64 +++++-- common/mist/mist_core.qip | 15 +- common/mist/mist_video.v | 121 +++++++++---- common/mist/osd.v | 63 ++++--- common/mist/rgb2ypbpr.v | 11 ++ common/mist/scandoubler.v | 131 +++++++------- common/mist/spdif.v | 337 ++++++++++++++++++++++++++++++++++++ common/mist/user_io.v | 59 +++++-- common/mist/video_cleaner.v | 104 +++++++++++ 15 files changed, 1072 insertions(+), 250 deletions(-) create mode 100644 common/mist/.gitignore create mode 100644 common/mist/i2c_master.v create mode 100644 common/mist/i2s.v create mode 100644 common/mist/spdif.v create mode 100644 common/mist/video_cleaner.v diff --git a/common/mist/.gitignore b/common/mist/.gitignore new file mode 100644 index 00000000..7664704b --- /dev/null +++ b/common/mist/.gitignore @@ -0,0 +1 @@ +*.bak \ No newline at end of file diff --git a/common/mist/cofi.sv b/common/mist/cofi.sv index c519bd5d..c49b55bb 100644 --- a/common/mist/cofi.sv +++ b/common/mist/cofi.sv @@ -21,7 +21,8 @@ module cofi ( output reg vs_out, output reg [VIDEO_DEPTH-1:0] red_out, output reg [VIDEO_DEPTH-1:0] green_out, - output reg [VIDEO_DEPTH-1:0] blue_out + output reg [VIDEO_DEPTH-1:0] blue_out, + output reg pix_ce_out ); parameter VIDEO_DEPTH=8; @@ -41,19 +42,23 @@ reg [VIDEO_DEPTH-1:0] green_last; reg [VIDEO_DEPTH-1:0] blue_last; wire ce = enable ? pix_ce : 1'b1; -always @(posedge clk) if (ce) begin - hblank_out <= hblank; - vblank_out <= vblank; - vs_out <= vs; - hs_out <= hs; +always @(posedge clk) begin + pix_ce_out <= 0; + if (ce) begin + hblank_out <= hblank; + vblank_out <= vblank; + vs_out <= vs; + hs_out <= hs; + pix_ce_out <= pix_ce; - red_last <= red; - blue_last <= blue; - green_last <= green; + red_last <= red; + blue_last <= blue; + green_last <= green; - red_out <= enable ? color_blend(red_last, red, hblank_out) : red; - blue_out <= enable ? color_blend(blue_last, blue, hblank_out) : blue; - green_out <= enable ? color_blend(green_last, green, hblank_out) : green; + red_out <= enable ? color_blend(red_last, red, hblank_out) : red; + blue_out <= enable ? color_blend(blue_last, blue, hblank_out) : blue; + green_out <= enable ? color_blend(green_last, green, hblank_out) : green; + end end endmodule diff --git a/common/mist/data_io.v b/common/mist/data_io.v index 6862f1f4..3563dad1 100644 --- a/common/mist/data_io.v +++ b/common/mist/data_io.v @@ -43,8 +43,8 @@ module data_io // Note: this is also set for user_io mounts. // Valid when ioctl_download = 1 or when img_mounted strobe is active in user_io. output reg ioctl_wr, // strobe indicating ioctl_dout valid - output reg [24:0] ioctl_addr, - output reg [7:0] ioctl_dout, + output reg [26:0] ioctl_addr, + output reg [((DOUT_16+1)*8)-1:0] ioctl_dout, input [7:0] ioctl_din, output reg [23:0] ioctl_fileext, // file extension output reg [31:0] ioctl_filesize, // file size @@ -56,7 +56,7 @@ module data_io input hdd_dat_req, output hdd_cdda_wr, output hdd_status_wr, - output [2:0] hdd_addr = 0, + output [2:0] hdd_addr, output hdd_wr, output [15:0] hdd_data_out, @@ -69,20 +69,21 @@ module data_io output [1:0] hdd1_ena ); -parameter START_ADDR = 25'd0; -parameter ROM_DIRECT_UPLOAD = 0; -parameter USE_QSPI = 0; -parameter ENABLE_IDE = 0; +parameter START_ADDR = 27'd0; +parameter ROM_DIRECT_UPLOAD = 1'b0; +parameter USE_QSPI = 1'b0; +parameter ENABLE_IDE = 1'b0; +parameter DOUT_16 = 1'b0; /////////////////////////////// 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 [14:0] sbuf; +reg [15:0] data_w; +reg [7:0] data_w2 = 0; +reg [15:0] data_w3 = 0; +reg [3:0] cnt; +reg [7:0] cmd; +reg [6:0] bytecnt; reg rclk = 0; reg rclk2 = 0; @@ -118,12 +119,14 @@ 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; + reg oe; if(SPI_SS2) begin + oe <= 0; reg_do <= 1'bZ; end else begin - if (cnt == 0) dout_r <= cmdcode; if (cnt == 15) begin + oe <= 1; case(cmd) CMD_IDE_REGS_RD, CMD_IDE_DATA_RD: @@ -135,12 +138,11 @@ always@(negedge SPI_SCK or posedge SPI_SS2) begin : SPI_TRANSMITTER DIO_FILE_RX_DAT: dout_r <= ioctl_din; - default: - dout_r <= 0; - + default: oe <= 0; endcase end - reg_do <= dout_r[~cnt[2:0]]; + + reg_do <= (!cnt[3] & ENABLE_IDE) ? cmdcode[~cnt[2:0]] : oe ? dout_r[~cnt[2:0]] : 1'bZ; end end @@ -150,16 +152,14 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER bytecnt <= 0; cnt <= 0; end else begin - // don't shift in last bit. It is evaluated directly - // when writing to ram - if(cnt != 15) sbuf <= { sbuf[5:0], SPI_DI}; + sbuf <= { sbuf[13:0], SPI_DI}; // count 0-7 8-15 8-15 ... if(cnt != 15) cnt <= cnt + 1'd1; else cnt <= 8; // finished command byte - if(cnt == 7) cmd <= {sbuf, SPI_DI}; + if(cnt == 7) cmd <= {sbuf[6:0], SPI_DI}; if(cnt == 15) begin if (~&bytecnt) bytecnt <= bytecnt + 1'd1; @@ -167,7 +167,7 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER case (cmd) // prepare/end transmission - DIO_FILE_TX: begin + DIO_FILE_TX: // prepare if(SPI_DI) begin addr_reset <= ~addr_reset; @@ -175,9 +175,8 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER end else begin downloading_reg <= 0; end - end - DIO_FILE_RX: begin + DIO_FILE_RX: // prepare if(SPI_DI) begin addr_reset <= ~addr_reset; @@ -185,31 +184,32 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER end else begin uploading_reg <= 0; end - end // command 0x57: DIO_FILE_RX_DAT // command 0x54: DIO_FILE_TX_DAT - DIO_FILE_RX_DAT, - DIO_FILE_TX_DAT: begin - data_w <= {sbuf, SPI_DI}; + DIO_FILE_RX_DAT: rclk <= ~rclk; - end + + DIO_FILE_TX_DAT: + if (bytecnt[0] | !DOUT_16) begin + data_w <= {sbuf, SPI_DI}; + rclk <= ~rclk; + end // expose file (menu) index - DIO_FILE_INDEX: ioctl_index <= {sbuf, SPI_DI}; + DIO_FILE_INDEX: ioctl_index <= {sbuf[6:0], SPI_DI}; // receiving FAT directory entry (mist-firmware/fat.h - DIRENTRY) - DIO_FILE_INFO: begin + DIO_FILE_INFO: case (bytecnt) - 8'h08: ioctl_fileext[23:16] <= {sbuf, SPI_DI}; - 8'h09: ioctl_fileext[15: 8] <= {sbuf, SPI_DI}; - 8'h0A: ioctl_fileext[ 7: 0] <= {sbuf, SPI_DI}; - 8'h1C: ioctl_filesize[ 7: 0] <= {sbuf, SPI_DI}; - 8'h1D: ioctl_filesize[15: 8] <= {sbuf, SPI_DI}; - 8'h1E: ioctl_filesize[23:16] <= {sbuf, SPI_DI}; - 8'h1F: ioctl_filesize[31:24] <= {sbuf, SPI_DI}; + 8'h08: ioctl_fileext[23:16] <= {sbuf[6:0], SPI_DI}; + 8'h09: ioctl_fileext[15: 8] <= {sbuf[6:0], SPI_DI}; + 8'h0A: ioctl_fileext[ 7: 0] <= {sbuf[6:0], SPI_DI}; + 8'h1C: ioctl_filesize[ 7: 0] <= {sbuf[6:0], SPI_DI}; + 8'h1D: ioctl_filesize[15: 8] <= {sbuf[6:0], SPI_DI}; + 8'h1E: ioctl_filesize[23:16] <= {sbuf[6:0], SPI_DI}; + 8'h1F: ioctl_filesize[31:24] <= {sbuf[6:0], SPI_DI}; endcase - end endcase end end @@ -255,31 +255,35 @@ endgenerate generate if (USE_QSPI) begin always@(negedge QSCK, posedge QCSn) begin : QSPI_RECEIVER - reg nibble_lo; + reg [1:0] nibble; reg cmd_got; reg cmd_write; if (QCSn) begin cmd_got <= 0; cmd_write <= 0; - nibble_lo <= 0; + nibble <= 0; end else begin - nibble_lo <= ~nibble_lo; - if (nibble_lo) begin - data_w3[3:0] <= QDAT; - if (!cmd_got) begin + nibble <= nibble + 1'd1; + data_w3 <= {data_w3[11:0], QDAT}; + if (!cmd_got) begin + // first byte is the command + if (nibble[0]) begin + nibble <= 0; cmd_got <= 1; - if ({data_w3[7:4], QDAT} == QSPI_WRITE) cmd_write <= 1; - end else begin - if (cmd_write) rclk3 <= ~rclk3; + if ({data_w3[3:0], QDAT} == QSPI_WRITE) cmd_write <= 1; end - end else - data_w3[7:4] <= QDAT; + end else if ((DOUT_16 && &nibble) || (!DOUT_16 & nibble[0])) begin + if (cmd_write) rclk3 <= ~rclk3; + end end end end endgenerate +reg wr_int, wr_int_direct, wr_int_qspi, rd_int; +wire [15:0] ioctl_dout_next = wr_int ? data_w : data_w3; + always@(posedge clk_sys) begin : DATA_OUT // synchronisers reg rclkD, rclkD2; @@ -287,9 +291,9 @@ always@(posedge clk_sys) begin : DATA_OUT reg rclk3D, rclk3D2; reg addr_resetD, addr_resetD2; - reg wr_int, wr_int_direct, wr_int_qspi, rd_int; - reg [24:0] addr; + reg [26:0] addr; reg [31:0] filepos; + reg [7:0] tmp; // bring flags from spi clock domain into core clock domain { rclkD, rclkD2 } <= { rclk, rclkD }; @@ -315,11 +319,31 @@ always@(posedge clk_sys) begin : DATA_OUT wr_int <= 0; wr_int_direct <= 0; 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; + if (wr_int_direct) begin + if (DOUT_16) begin + if (addr[0]) begin + ioctl_dout <= {data_w2, tmp}; + ioctl_wr <= 1; + ioctl_addr <= {addr[26:1], 1'b0}; + end else + tmp <= data_w2; + end else begin + ioctl_dout <= data_w2; + ioctl_wr <= 1; + ioctl_addr <= addr; + end addr <= addr + 1'd1; + end + if (wr_int | wr_int_qspi) begin + ioctl_wr <= 1; ioctl_addr <= addr; + if (DOUT_16) begin + ioctl_dout <= {ioctl_dout_next[7:0], ioctl_dout_next[15:8]}; + addr <= addr + 2'd2; + end else begin + ioctl_dout <= ioctl_dout_next[7:0]; + addr <= addr + 1'd1; + end end if (rd_int) begin ioctl_addr <= ioctl_addr + 1'd1; @@ -383,7 +407,7 @@ 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; +reg [15:0] data_ide; always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER_IDE if(SPI_SS2) begin @@ -403,13 +427,13 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER_IDE CMD_IDE_STATUS_WR: if (bytecnt == 0) begin - data_ide <= {sbuf, SPI_DI}; + data_ide[7:0] <= {sbuf[6:0], 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}; + data_ide[7:0] <= {sbuf[6:0], SPI_DI}; rclk_ide_regs_wr <= ~rclk_ide_regs_wr; end @@ -419,13 +443,13 @@ always@(posedge SPI_SCK, posedge SPI_SS2) begin : SPI_RECEIVER_IDE end CMD_IDE_DATA_WR: - if (bytecnt > 4) begin + if (bytecnt > 4 & !bytecnt[0]) begin data_ide <= {sbuf, SPI_DI}; rclk_ide_wr <= ~rclk_ide_wr; end CMD_IDE_CDDA_WR: - if (bytecnt > 4) begin + if (bytecnt > 4 & !bytecnt[0]) begin data_ide <= {sbuf, SPI_DI}; rclk_cdda_wr <= ~rclk_cdda_wr; end @@ -483,7 +507,7 @@ always@(posedge hdd_clk) begin : IDE_OUT if (rclk_ide_statD ^ rclk_ide_statD2) begin int_hdd_status_wr <= 1; - int_hdd_data_out <= {8'h00, data_ide}; + int_hdd_data_out <= {8'h00, data_ide[7:0]}; end if (rclk_ide_rdD ^ rclk_ide_rdD2) begin loword <= ~loword; @@ -491,23 +515,15 @@ always@(posedge hdd_clk) begin : IDE_OUT 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 + int_hdd_data_out <= data_ide; + int_hdd_data_wr <= 1; 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 + int_hdd_data_out <= data_ide; + int_hdd_cdda_wr <= 1; end + if (rclk2D ^ rclk2D2 && !downloading_reg) begin loword <= ~loword; if (!loword) @@ -519,7 +535,7 @@ always@(posedge hdd_clk) begin : IDE_OUT 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_data_out <= {8'h00, data_ide[7:0]}; int_hdd_addr <= int_hdd_addr + 1'd1; end if (rclk_ide_regs_rdD ^ rclk_ide_regs_rdD2) begin diff --git a/common/mist/i2c_master.v b/common/mist/i2c_master.v new file mode 100644 index 00000000..1ba6c1a3 --- /dev/null +++ b/common/mist/i2c_master.v @@ -0,0 +1,111 @@ +// taken and tweaked from MiSTer sys/ +module i2c_master +( + input CLK, + + input I2C_START, + input I2C_READ, + input [6:0] I2C_ADDR, + input [7:0] I2C_SUBADDR, + input [7:0] I2C_WDATA, + output [7:0] I2C_RDATA, + output reg I2C_END = 1, + output reg I2C_ACK = 0, + + //I2C bus + inout I2C_SCL, + inout I2C_SDA +); + + +// Clock Setting +parameter CLK_Freq = 50_000_000; // 50 MHz +parameter I2C_Freq = 400_000; // 400 KHz + +localparam I2C_FreqX2 = I2C_Freq*2; + +reg I2C_CLOCK; +reg [31:0] cnt; +wire [31:0] cnt_next = cnt + I2C_FreqX2; + +always @(posedge CLK) begin + cnt <= cnt_next; + if(cnt_next >= CLK_Freq) begin + cnt <= cnt_next - CLK_Freq; + I2C_CLOCK <= ~I2C_CLOCK; + end +end + +reg SCLK; +reg [11:0] SDO; +reg [0:7] rdata; + +reg [6:0] SD_COUNTER; +reg [0:42] SD; + +assign I2C_SCL = (SCLK | I2C_CLOCK) ? 1'bZ : 1'b0; +assign I2C_SDA = ((CLK_Freq/I2C_Freq) > 250 ? SDO[11] : ((CLK_Freq/I2C_Freq) > 20 ? SDO[7] : SDO[3])) ? 1'bZ : 1'b0; + +initial begin + SD_COUNTER = 'b1111111; + SD = {40'hFFFFFFFFFF, 3'b111}; + SCLK = 1; + SDO = 12'hFFF; +end + +assign I2C_RDATA = rdata; + +always @(posedge CLK) begin + reg old_clk; + reg old_st; + reg rd; + reg sda_in; + + old_clk <= I2C_CLOCK; + old_st <= I2C_START; + sda_in <= I2C_SDA; + + // delay to make sure SDA changed while SCL is stabilized at low + if(old_clk && ~I2C_CLOCK && ~SD_COUNTER[6]) SDO[0] <= SD[SD_COUNTER[5:0]]; + SDO[11:1] <= SDO[10:0]; + + if(~old_st && I2C_START) begin + SCLK <= 1; + SDO <= 12'hFFF; + I2C_ACK <= 0; + I2C_END <= 0; + rd <= I2C_READ; + if(I2C_READ) SD[0:42] <= {2'b10, I2C_ADDR, 1'b0, 1'b1, I2C_SUBADDR, 3'b110, I2C_ADDR, 1'b1, 1'b1, 8'b11111111, 4'b1011}; + else SD[0:31] <= {2'b10, I2C_ADDR, 1'b0, 1'b1, I2C_SUBADDR, 1'b1, I2C_WDATA, 4'b1011}; + SD_COUNTER <= 0; + end else begin + if(~old_clk && I2C_CLOCK && ~SD_COUNTER[6]) begin + SD_COUNTER <= SD_COUNTER + 6'd1; + case(SD_COUNTER) + 01: SCLK <= 0; + 10: I2C_ACK <= ~sda_in; + 19: I2C_ACK <= ~sda_in; + 20: if (rd) SCLK <= 1; // repeated start + 21: if (rd) SCLK <= 0; + 28: if (~rd) I2C_ACK <= ~sda_in; + 29: if (~rd) SCLK <= 1; + 30: if (rd) I2C_ACK <= ~sda_in; + 32: if (~rd) begin + I2C_END <= 1; + SD_COUNTER <= 64; + end + 40: SCLK <= 1; + 42: begin + I2C_END <= 1; + SD_COUNTER <= 64; + end + 64: SCLK <= 1; + default: ; + endcase + + if(SD_COUNTER >= 31 && SD_COUNTER <= 38) rdata[SD_COUNTER[5:0]-31] <= sda_in; + end + end +end + +endmodule \ No newline at end of file diff --git a/common/mist/i2s.v b/common/mist/i2s.v new file mode 100644 index 00000000..19b70c40 --- /dev/null +++ b/common/mist/i2s.v @@ -0,0 +1,72 @@ +// taken and tweaked from MiSTer sys/ + +module i2s +( + input reset, + input clk, + input [31:0] clk_rate, + + output reg sclk, + output reg lrclk, + output reg sdata, + + input [AUDIO_DW-1:0] left_chan, + input [AUDIO_DW-1:0] right_chan +); + +// Clock Setting +parameter I2S_Freq = 48_000; // 48 KHz +parameter AUDIO_DW = 16; + +localparam I2S_FreqX2 = I2S_Freq*2*AUDIO_DW*2; + +reg [31:0] cnt; +wire [31:0] cnt_next = cnt + I2S_FreqX2; + +reg ce; + +always @(posedge clk) begin + ce <= 0; + cnt <= cnt_next; + if(cnt_next >= clk_rate) begin + cnt <= cnt_next - clk_rate; + ce <= 1; + end +end + + +always @(posedge clk) begin + reg [4:0] bit_cnt = 1; + + reg [AUDIO_DW-1:0] left; + reg [AUDIO_DW-1:0] right; + + if (reset) begin + bit_cnt <= 1; + lrclk <= 1; + sclk <= 1; + sdata <= 1; + sclk <= 1; + end + else begin + if(ce) begin + sclk <= ~sclk; + if(sclk) begin + if(bit_cnt == AUDIO_DW) begin + bit_cnt <= 1; + lrclk <= ~lrclk; + if(lrclk) begin + left <= left_chan; + right <= right_chan; + end + end + else begin + bit_cnt <= bit_cnt + 1'd1; + end + sdata <= lrclk ? right[AUDIO_DW - bit_cnt] : left[AUDIO_DW - bit_cnt]; + end + end + end +end + +endmodule diff --git a/common/mist/mist.qip b/common/mist/mist.qip index 923b5289..a3eb1346 100644 --- a/common/mist/mist.qip +++ b/common/mist/mist.qip @@ -1,14 +1,18 @@ -set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) data_io.v] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v] -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] +set_global_assignment -library mist -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) data_io.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) osd.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) arcade_inputs.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) video_cleaner.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) rgb2ypbpr.v] +set_global_assignment -library mist -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) cofi.sv] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) sd_card.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) ide.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) ide_fifo.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) cdda_fifo.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) i2c_master.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) i2s.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) spdif.v] +set_global_assignment -library mist -name VHDL_FILE [file join $::quartus(qip_path) dac.vhd] diff --git a/common/mist/mist.vhd b/common/mist/mist.vhd index 10d8af49..d69dab4a 100644 --- a/common/mist/mist.vhd +++ b/common/mist/mist.vhd @@ -78,20 +78,33 @@ port ( mouse_z : out signed(3 downto 0); mouse_flags : out std_logic_vector(7 downto 0); -- YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn mouse_strobe : out std_logic; - mouse_idx : out std_logic + mouse_idx : out std_logic; + + i2c_start : out std_logic; + i2c_read : out std_logic; + i2c_addr : out std_logic_vector(6 downto 0); + i2c_subaddr : out std_logic_vector(7 downto 0); + i2c_dout : out std_logic_vector(7 downto 0); + i2c_din : in std_logic_vector(7 downto 0) := (others => '0'); + i2c_end : in std_logic := '0'; + i2c_ack : in std_logic := '0' ); end component user_io; component mist_video generic ( - OSD_COLOR : std_logic_vector(2 downto 0) := "110"; - OSD_X_OFFSET : std_logic_vector(9 downto 0) := (others => '0'); - OSD_Y_OFFSET : std_logic_vector(9 downto 0) := (others => '0'); - SD_HCNT_WIDTH: integer := 9; - COLOR_DEPTH : integer := 6; - OSD_AUTO_CE : boolean := true; - SYNC_AND : boolean := false; - USE_BLANKS : boolean := false + OSD_COLOR : std_logic_vector(2 downto 0) := "110"; + OSD_X_OFFSET : std_logic_vector(9 downto 0) := (others => '0'); + OSD_Y_OFFSET : std_logic_vector(9 downto 0) := (others => '0'); + SD_HCNT_WIDTH : integer := 9; + COLOR_DEPTH : integer := 6; + OSD_AUTO_CE : boolean := true; + SYNC_AND : boolean := false; + USE_BLANKS : boolean := false; + SD_HSCNT_WIDTH : integer := 12; + OUT_COLOR_DEPTH : integer := 6; + BIG_OSD : boolean := false; + VIDEO_CLEANER : boolean := false ); port ( clk_sys : in std_logic; @@ -118,10 +131,37 @@ port ( VGA_HS : out std_logic; VGA_VS : out std_logic; - VGA_R : out std_logic_vector(5 downto 0); - VGA_G : out std_logic_vector(5 downto 0); - VGA_B : out std_logic_vector(5 downto 0) + VGA_HB : out std_logic; + VGA_VB : out std_logic; + VGA_DE : out std_logic; + VGA_R : out std_logic_vector(OUT_COLOR_DEPTH-1 downto 0); + VGA_G : out std_logic_vector(OUT_COLOR_DEPTH-1 downto 0); + VGA_B : out std_logic_vector(OUT_COLOR_DEPTH-1 downto 0) ); end component mist_video; +component i2c_master +generic ( + CLK_Freq : integer := 50000000; + I2C_Freq : integer := 400000 +); +port ( + CLK : in std_logic; + + I2C_START : in std_logic; + I2C_READ : in std_logic; + I2C_ADDR : in std_logic_vector(6 downto 0); + I2C_SUBADDR : in std_logic_vector(7 downto 0); + I2C_WDATA : in std_logic_vector(7 downto 0); + I2C_RDATA : out std_logic_vector(7 downto 0); + I2C_END : out std_logic; + I2C_ACK : out std_logic; + + -- I2C bus + I2C_SCL : inout std_logic; + I2C_SDA : inout std_logic +); + +end component i2c_master; + end package; diff --git a/common/mist/mist_core.qip b/common/mist/mist_core.qip index 1de1ee1c..453f00b7 100644 --- a/common/mist/mist_core.qip +++ b/common/mist/mist_core.qip @@ -1,7 +1,8 @@ -set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v] -set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.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 -library mist -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) osd.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) video_cleaner.v] +set_global_assignment -library mist -name VERILOG_FILE [file join $::quartus(qip_path) rgb2ypbpr.v] +set_global_assignment -library mist -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) cofi.sv] diff --git a/common/mist/mist_video.v b/common/mist/mist_video.v index 7e90f157..391d3eb4 100644 --- a/common/mist/mist_video.v +++ b/common/mist/mist_video.v @@ -41,25 +41,32 @@ module mist_video input VSync, // MiST video output signals - output reg [5:0] VGA_R, - output reg [5:0] VGA_G, - output reg [5:0] VGA_B, + output reg [OUT_COLOR_DEPTH-1:0] VGA_R, + output reg [OUT_COLOR_DEPTH-1:0] VGA_G, + output reg [OUT_COLOR_DEPTH-1:0] VGA_B, output reg VGA_VS, - output reg VGA_HS + output reg VGA_HS, + output reg VGA_HB, + output reg VGA_VB, + output reg VGA_DE ); parameter OSD_COLOR = 3'd4; parameter OSD_X_OFFSET = 10'd0; parameter OSD_Y_OFFSET = 10'd0; parameter SD_HCNT_WIDTH = 9; -parameter COLOR_DEPTH = 6; // 1-6 +parameter COLOR_DEPTH = 6; // 1-8 parameter OSD_AUTO_CE = 1'b1; -parameter SYNC_AND = 1'b0; // 0 - XOR, 1 - AND -parameter USE_BLANKS = 1'b0; // Honor H/VBlank signals? +parameter SYNC_AND = 1'b0; // 0 - XOR, 1 - AND +parameter USE_BLANKS = 1'b0; // Honor H/VBlank signals? +parameter SD_HSCNT_WIDTH = 12; +parameter OUT_COLOR_DEPTH = 6; // 1-8 +parameter BIG_OSD = 1'b0; // 16 line OSD +parameter VIDEO_CLEANER = 1'b0; // Align VSync/VBlank to HSync/HBlank edges. HDMI usually needs it. -wire [5:0] SD_R_O; -wire [5:0] SD_G_O; -wire [5:0] SD_B_O; +wire [OUT_COLOR_DEPTH-1:0] SD_R_O; +wire [OUT_COLOR_DEPTH-1:0] SD_G_O; +wire [OUT_COLOR_DEPTH-1:0] SD_B_O; wire SD_HS_O; wire SD_VS_O; wire SD_HB_O; @@ -67,7 +74,7 @@ wire SD_VB_O; wire pixel_ena; -scandoubler #(SD_HCNT_WIDTH, COLOR_DEPTH) scandoubler +scandoubler #(SD_HCNT_WIDTH, COLOR_DEPTH, SD_HSCNT_WIDTH, OUT_COLOR_DEPTH) scandoubler ( .clk_sys ( clk_sys ), .bypass ( scandoubler_disable ), @@ -90,11 +97,11 @@ scandoubler #(SD_HCNT_WIDTH, COLOR_DEPTH) scandoubler .b_out ( SD_B_O ) ); -wire [5:0] osd_r_o; -wire [5:0] osd_g_o; -wire [5:0] osd_b_o; +wire [OUT_COLOR_DEPTH-1:0] osd_r_o; +wire [OUT_COLOR_DEPTH-1:0] osd_g_o; +wire [OUT_COLOR_DEPTH-1:0] osd_b_o; -osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR, OSD_AUTO_CE, USE_BLANKS) osd +osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR, OSD_AUTO_CE, USE_BLANKS, OUT_COLOR_DEPTH, BIG_OSD) osd ( .clk_sys ( clk_sys ), .rotate ( rotate ), @@ -102,9 +109,9 @@ osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR, OSD_AUTO_CE, USE_BLANKS) osd .SPI_DI ( SPI_DI ), .SPI_SCK ( SPI_SCK ), .SPI_SS3 ( SPI_SS3 ), - .R_in ( SD_R_O ), - .G_in ( SD_G_O ), - .B_in ( SD_B_O ), + .R_in ( SD_R_O ), + .G_in ( SD_G_O ), + .B_in ( SD_B_O ), .HBlank ( SD_HB_O ), .VBlank ( SD_VB_O ), .HSync ( SD_HS_O ), @@ -114,14 +121,17 @@ osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR, OSD_AUTO_CE, USE_BLANKS) osd .B_out ( osd_b_o ) ); -wire [5:0] cofi_r, cofi_g, cofi_b; +wire [OUT_COLOR_DEPTH-1:0] cofi_r, cofi_g, cofi_b; wire cofi_hs, cofi_vs; +wire cofi_hb, cofi_vb; +wire cofi_pixel_ena; -cofi #(6) cofi ( +cofi #(OUT_COLOR_DEPTH) cofi ( .clk ( clk_sys ), .pix_ce ( pixel_ena ), .enable ( blend ), .hblank ( USE_BLANKS ? SD_HB_O : ~SD_HS_O ), + .vblank ( SD_VB_O ), .hs ( SD_HS_O ), .vs ( SD_VS_O ), .red ( osd_r_o ), @@ -129,34 +139,71 @@ cofi #(6) cofi ( .blue ( osd_b_o ), .hs_out ( cofi_hs ), .vs_out ( cofi_vs ), + .hblank_out( cofi_hb ), + .vblank_out( cofi_vb ), .red_out ( cofi_r ), .green_out( cofi_g ), - .blue_out( cofi_b ) + .blue_out( cofi_b ), + .pix_ce_out(cofi_pixel_ena) +); + +wire [OUT_COLOR_DEPTH-1:0] cleaner_r_o; +wire [OUT_COLOR_DEPTH-1:0] cleaner_g_o; +wire [OUT_COLOR_DEPTH-1:0] cleaner_b_o; +wire cleaner_hs_o, cleaner_vs_o, cleaner_hb_o, cleaner_vb_o; + +video_cleaner #(OUT_COLOR_DEPTH) video_cleaner( + .clk_vid ( clk_sys ), + .ce_pix ( scandoubler_disable ? 1'b1 : cofi_pixel_ena ), + .enable ( VIDEO_CLEANER ), + + .R ( cofi_r ), + .G ( cofi_g ), + .B ( cofi_b ), + + .HSync ( cofi_hs ), + .VSync ( cofi_vs ), + .HBlank ( cofi_hb ), + .VBlank ( cofi_vb ), + + .VGA_R ( cleaner_r_o ), + .VGA_G ( cleaner_g_o ), + .VGA_B ( cleaner_b_o ), + .VGA_VS ( cleaner_vs_o), + .VGA_HS ( cleaner_hs_o), + .HBlank_out ( cleaner_hb_o), + .VBlank_out ( cleaner_vb_o) ); wire hs, vs, cs; -wire [5:0] r,g,b; +wire hb, vb; +wire [OUT_COLOR_DEPTH-1:0] r,g,b; -RGBtoYPbPr #(6) rgb2ypbpr +RGBtoYPbPr #(OUT_COLOR_DEPTH) rgb2ypbpr ( .clk ( clk_sys ), .ena ( ypbpr ), - .red_in ( cofi_r ), - .green_in ( cofi_g ), - .blue_in ( cofi_b ), - .hs_in ( cofi_hs ), - .vs_in ( cofi_vs ), - .cs_in ( SYNC_AND ? (cofi_hs & cofi_vs) : ~(cofi_hs ^ cofi_vs) ), - .red_out ( r ), - .green_out ( g ), - .blue_out ( b ), - .hs_out ( hs ), - .vs_out ( vs ), - .cs_out ( cs ) + .red_in ( cleaner_r_o ), + .green_in ( cleaner_g_o ), + .blue_in ( cleaner_b_o ), + .hs_in ( cleaner_hs_o ), + .vs_in ( cleaner_vs_o ), + .cs_in ( SYNC_AND ? (cleaner_hs_o & cleaner_vs_o) : ~(cleaner_hs_o ^ cleaner_vs_o) ), + .hb_in ( cleaner_hb_o ), + .vb_in ( cleaner_vb_o ), + .red_out ( r ), + .green_out ( g ), + .blue_out ( b ), + .hs_out ( hs ), + .vs_out ( vs ), + .cs_out ( cs ), + .hb_out ( hb ), + .vb_out ( vb ) ); always @(posedge clk_sys) begin + VGA_R <= r; VGA_G <= g; VGA_B <= b; @@ -164,5 +211,9 @@ always @(posedge clk_sys) begin // and VCC on VGA_VS (to switch into rgb mode) VGA_HS <= ((~no_csync & scandoubler_disable) || ypbpr)? cs : hs; VGA_VS <= ((~no_csync & scandoubler_disable) || ypbpr)? 1'b1 : vs; + + VGA_HB <= hb; + VGA_VB <= vb; + VGA_DE <= ~(hb | vb); end endmodule diff --git a/common/mist/osd.v b/common/mist/osd.v index e7b769e7..dea543e8 100644 --- a/common/mist/osd.v +++ b/common/mist/osd.v @@ -15,18 +15,18 @@ module osd ( input [1:0] rotate, //[0] - rotate [1] - left or right // VGA signals coming from core - input [5:0] R_in, - input [5:0] G_in, - input [5:0] B_in, + input [OUT_COLOR_DEPTH-1:0] R_in, + input [OUT_COLOR_DEPTH-1:0] G_in, + input [OUT_COLOR_DEPTH-1:0] B_in, input HBlank, input VBlank, input HSync, input VSync, // VGA signals going to video connector - output [5:0] R_out, - output [5:0] G_out, - output [5:0] B_out + output [OUT_COLOR_DEPTH-1:0] R_out, + output [OUT_COLOR_DEPTH-1:0] G_out, + output [OUT_COLOR_DEPTH-1:0] B_out ); parameter OSD_X_OFFSET = 11'd0; @@ -34,9 +34,12 @@ parameter OSD_Y_OFFSET = 11'd0; parameter OSD_COLOR = 3'd0; parameter OSD_AUTO_CE = 1'b1; parameter USE_BLANKS = 1'b0; +parameter OUT_COLOR_DEPTH = 6; +parameter BIG_OSD = 1'b0; localparam OSD_WIDTH = 11'd256; localparam OSD_HEIGHT = 11'd128; +localparam OSD_LINES = 8 << BIG_OSD; localparam OSD_WIDTH_PADDED = OSD_WIDTH + (OSD_WIDTH >> 1); // 25% padding left and right @@ -47,12 +50,12 @@ localparam OSD_WIDTH_PADDED = OSD_WIDTH + (OSD_WIDTH >> 1); // 25% padding left // this core supports only the display related OSD commands // of the minimig reg osd_enable; -(* ramstyle = "no_rw_check" *) reg [7:0] osd_buffer[2047:0]; // the OSD buffer itself +(* ramstyle = "no_rw_check" *) reg [7:0] osd_buffer[256*OSD_LINES-1:0]; // the OSD buffer itself // the OSD has its own SPI interface to the io controller always@(posedge SPI_SCK, posedge SPI_SS3) begin reg [4:0] cnt; - reg [10:0] bcnt; + reg [11:0] bcnt; reg [7:0] sbuf; reg [7:0] cmd; @@ -69,15 +72,15 @@ always@(posedge SPI_SCK, posedge SPI_SS3) begin if(cnt == 7) begin cmd <= {sbuf[6:0], SPI_DI}; - // lower three command bits are line address - bcnt <= {sbuf[1:0], SPI_DI, 8'h00}; + // lower four command bits are line address + bcnt <= {sbuf[2:0], SPI_DI, 8'h00}; // command 0x40: OSDCMDENABLE, OSDCMDDISABLE if(sbuf[6:3] == 4'b0100) osd_enable <= SPI_DI; end // command 0x20: OSDCMDWRITE - if((cmd[7:3] == 5'b00100) && (cnt == 15)) begin + if((cmd[7:4] == 4'b0010) && (cnt == 15)) begin osd_buffer[bcnt] <= {sbuf[6:0], SPI_DI}; bcnt <= bcnt + 1'd1; end @@ -103,20 +106,23 @@ wire [10:0] dsp_height = (vs_pol & !USE_BLANKS) ? vs_low : vs_high; wire doublescan = (dsp_height>350); reg auto_ce_pix; -always @(posedge clk_sys) begin +always @(posedge clk_sys) begin : cedetect reg [15:0] cnt = 0; reg [2:0] pixsz; reg [2:0] pixcnt; reg hs; + reg hb; cnt <= cnt + 1'd1; hs <= HSync; + hb <= HBlank; pixcnt <= pixcnt + 1'd1; if(pixcnt == pixsz) pixcnt <= 0; auto_ce_pix <= !pixcnt; - if(hs && ~HSync) begin + if((!USE_BLANKS && hs && ~HSync) || + ( USE_BLANKS && ~hb && HBlank)) begin cnt <= 0; if(cnt <= OSD_WIDTH_PADDED * 2) pixsz <= 0; else if(cnt <= OSD_WIDTH_PADDED * 3) pixsz <= 1; @@ -128,6 +134,7 @@ always @(posedge clk_sys) begin pixcnt <= 0; auto_ce_pix <= 1; end + if (USE_BLANKS && HBlank) cnt <= 0; end wire ce_pix = OSD_AUTO_CE ? auto_ce_pix : ce; @@ -206,19 +213,29 @@ wire [10:0] osd_vcnt = v_cnt - v_osd_start; wire [10:0] osd_hcnt_next = osd_hcnt + 2'd1; // one pixel offset for osd byte address register reg osd_de; -reg [10:0] osd_buffer_addr; +reg [11:0] osd_buffer_addr; wire [7:0] osd_byte = osd_buffer[osd_buffer_addr]; reg osd_pixel; always @(posedge clk_sys) begin if(ce_pix) begin - osd_buffer_addr <= rotate[0] ? {rotate[1] ? osd_hcnt_next[7:5] : ~osd_hcnt_next[7:5], - rotate[1] ? (doublescan ? ~osd_vcnt[7:0] : ~{osd_vcnt[6:0], 1'b0}) : - (doublescan ? osd_vcnt[7:0] : {osd_vcnt[6:0], 1'b0})} : - {doublescan ? osd_vcnt[7:5] : osd_vcnt[6:4], osd_hcnt_next[7:0]}; + if (!BIG_OSD) begin + osd_buffer_addr <= rotate[0] ? {rotate[1] ? osd_hcnt_next[7:5] : ~osd_hcnt_next[7:5], + rotate[1] ? (doublescan ? ~osd_vcnt[7:0] : ~{osd_vcnt[6:0], 1'b0}) : + (doublescan ? osd_vcnt[7:0] : {osd_vcnt[6:0], 1'b0})} : + {doublescan ? osd_vcnt[7:5] : osd_vcnt[6:4], osd_hcnt_next[7:0]}; - osd_pixel <= rotate[0] ? osd_byte[rotate[1] ? osd_hcnt[4:2] : ~osd_hcnt[4:2]] : - osd_byte[doublescan ? osd_vcnt[4:2] : osd_vcnt[3:1]]; + osd_pixel <= rotate[0] ? osd_byte[rotate[1] ? osd_hcnt[4:2] : ~osd_hcnt[4:2]] : + osd_byte[doublescan ? osd_vcnt[4:2] : osd_vcnt[3:1]]; + end else begin + osd_buffer_addr <= rotate[0] ? {rotate[1] ? osd_hcnt_next[7:4] : ~osd_hcnt_next[7:4], + rotate[1] ? (doublescan ? ~osd_vcnt[7:0] : ~{osd_vcnt[6:0], 1'b0}) : + (doublescan ? osd_vcnt[7:0] : {osd_vcnt[6:0], 1'b0})} : + {doublescan ? osd_vcnt[7:4] : osd_vcnt[6:3], osd_hcnt_next[7:0]}; + + osd_pixel <= rotate[0] ? osd_byte[rotate[1] ? osd_hcnt[3:1] : ~osd_hcnt[3:1]] : + osd_byte[doublescan ? osd_vcnt[3:1] : osd_vcnt[2:0]]; + end osd_de <= osd_enable && ((USE_BLANKS && !HBlank) || (!USE_BLANKS && HSync != hs_pol)) && (h_cnt >= h_osd_start) && (h_cnt < h_osd_end) && @@ -226,8 +243,8 @@ always @(posedge clk_sys) begin end end -assign R_out = !osd_de ? R_in : {osd_pixel, osd_pixel, OSD_COLOR[2], R_in[5:3]}; -assign G_out = !osd_de ? G_in : {osd_pixel, osd_pixel, OSD_COLOR[1], G_in[5:3]}; -assign B_out = !osd_de ? B_in : {osd_pixel, osd_pixel, OSD_COLOR[0], B_in[5:3]}; +assign R_out = !osd_de ? R_in : {osd_pixel, osd_pixel, OSD_COLOR[2], R_in[OUT_COLOR_DEPTH-1:3]}; +assign G_out = !osd_de ? G_in : {osd_pixel, osd_pixel, OSD_COLOR[1], G_in[OUT_COLOR_DEPTH-1:3]}; +assign B_out = !osd_de ? B_in : {osd_pixel, osd_pixel, OSD_COLOR[0], B_in[OUT_COLOR_DEPTH-1:3]}; endmodule diff --git a/common/mist/rgb2ypbpr.v b/common/mist/rgb2ypbpr.v index ee3afc1a..b96c136f 100644 --- a/common/mist/rgb2ypbpr.v +++ b/common/mist/rgb2ypbpr.v @@ -2,6 +2,7 @@ // Copyright 2020/2021 by Alastair M. Robinson +(* altera_attribute = "-name AUTO_SHIFT_REGISTER_RECOGNITION OFF" *) module RGBtoYPbPr ( input clk, @@ -12,6 +13,8 @@ module RGBtoYPbPr input [WIDTH-1:0] blue_in, input hs_in, input vs_in, + input hb_in, + input vb_in, input cs_in, input pixel_in, @@ -20,6 +23,8 @@ module RGBtoYPbPr output [WIDTH-1:0] blue_out, output reg hs_out, output reg vs_out, + output reg hb_out, + output reg vb_out, output reg cs_out, output reg pixel_out ); @@ -46,6 +51,8 @@ reg hs_d; reg vs_d; reg cs_d; reg pixel_d; +reg hb_d; +reg vb_d; assign red_out = r[8+WIDTH-1:8]; assign green_out = y[8+WIDTH-1:8]; @@ -57,6 +64,8 @@ always @(posedge clk) begin vs_d <= vs_in; // so they're delayed the same amount as the incoming video cs_d <= cs_in; pixel_d <= pixel_in; + hb_d <= hb_in; + vb_d <= vb_in; if(ena) begin // (Y = 0.299*R + 0.587*G + 0.114*B) @@ -88,6 +97,8 @@ always @(posedge clk) begin vs_out <= vs_d; cs_out <= cs_d; pixel_out <= pixel_d; + hb_out <= hb_d; + vb_out <= vb_d; if(ena) begin y <= r_y + g_y + b_y; diff --git a/common/mist/scandoubler.v b/common/mist/scandoubler.v index 8f98ff8e..6c1384c9 100644 --- a/common/mist/scandoubler.v +++ b/common/mist/scandoubler.v @@ -16,8 +16,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// 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 @@ -56,48 +54,48 @@ module scandoubler output vb_out, output hs_out, output vs_out, - output [5:0] r_out, - output [5:0] g_out, - output [5:0] b_out + output [OUT_COLOR_DEPTH-1:0] r_out, + output [OUT_COLOR_DEPTH-1:0] g_out, + output [OUT_COLOR_DEPTH-1:0] b_out ); 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 +parameter OUT_COLOR_DEPTH = 6; // Bits per color outputted // --------------------- create output signals ----------------- // latch everything once more to make it glitch free and apply scanline effect reg scanline; -reg [5:0] r; -reg [5:0] g; -reg [5:0] b; +reg [OUT_COLOR_DEPTH-1:0] r; +reg [OUT_COLOR_DEPTH-1:0] g; +reg [OUT_COLOR_DEPTH-1:0] b; wire [COLOR_DEPTH*3-1:0] sd_mux = bypass ? {r_in, g_in, b_in} : sd_out[COLOR_DEPTH*3-1:0]; +localparam m = OUT_COLOR_DEPTH/COLOR_DEPTH; +localparam n = OUT_COLOR_DEPTH%COLOR_DEPTH; + always @(*) begin - if (COLOR_DEPTH == 6) begin - 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_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_mux[0]}}; - g = {6{sd_mux[1]}}; - r = {6{sd_mux[2]}}; + if (n>0) begin + b = { {m{sd_mux[COLOR_DEPTH-1:0]}}, sd_mux[COLOR_DEPTH-1 -:n] }; + g = { {m{sd_mux[COLOR_DEPTH*2-1:COLOR_DEPTH]}}, sd_mux[COLOR_DEPTH*2-1 -:n] }; + r = { {m{sd_mux[COLOR_DEPTH*3-1:COLOR_DEPTH*2]}}, sd_mux[COLOR_DEPTH*3-1 -:n] }; end else begin - 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)] }; + b = { {m{sd_mux[COLOR_DEPTH-1:0]}} }; + g = { {m{sd_mux[COLOR_DEPTH*2-1:COLOR_DEPTH]}} }; + r = { {m{sd_mux[COLOR_DEPTH*3-1:COLOR_DEPTH*2]}} }; end end -reg [12:0] r_mul; -reg [12:0] g_mul; -reg [12:0] b_mul; +reg [OUT_COLOR_DEPTH+6:0] r_mul; +reg [OUT_COLOR_DEPTH+6:0] g_mul; +reg [OUT_COLOR_DEPTH+6:0] b_mul; +reg hb_o; +reg vb_o; +reg hs_o; +reg vs_o; wire scanline_bypass = (!scanline) | (!(|scanlines)) | bypass; @@ -113,7 +111,9 @@ wire [6:0] scanline_coeff = scanline_bypass ? always @(posedge clk_sys) begin if(ce_x2) begin hs_o <= hs_sd; - vs_o <= vs_in; + vs_o <= vs_sd; + hb_o <= hb_sd; + vb_o <= vb_sd; // reset scanlines at every new screen if(vs_o != vs_in) scanline <= 0; @@ -127,36 +127,30 @@ always @(posedge clk_sys) begin end end -wire [5:0] r_o = r_mul[11:6]; -wire [5:0] g_o = g_mul[11:6]; -wire [5:0] b_o = b_mul[11:6]; -wire hb_o = hb_sd; -wire vb_o = vb_sd; -reg hs_o; -reg vs_o; +wire [OUT_COLOR_DEPTH-1:0] r_o = r_mul[OUT_COLOR_DEPTH+5 -:OUT_COLOR_DEPTH]; +wire [OUT_COLOR_DEPTH-1:0] g_o = g_mul[OUT_COLOR_DEPTH+5 -:OUT_COLOR_DEPTH]; +wire [OUT_COLOR_DEPTH-1:0] b_o = b_mul[OUT_COLOR_DEPTH+5 -:OUT_COLOR_DEPTH]; // Output multiplexing wire blank_out = hb_out | vb_out; -assign r_out = blank_out ? {COLOR_DEPTH{1'b0}} : bypass ? r : r_o; -assign g_out = blank_out ? {COLOR_DEPTH{1'b0}} : bypass ? g : g_o; -assign b_out = blank_out ? {COLOR_DEPTH{1'b0}} : bypass ? b : b_o; +assign r_out = blank_out ? {OUT_COLOR_DEPTH{1'b0}} : bypass ? r : r_o; +assign g_out = blank_out ? {OUT_COLOR_DEPTH{1'b0}} : bypass ? g : g_o; +assign b_out = blank_out ? {OUT_COLOR_DEPTH{1'b0}} : bypass ? b : b_o; assign hb_out = bypass ? hb_in : hb_o; assign vb_out = bypass ? vb_in : vb_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 -reg [3+COLOR_DEPTH*3-1:0] sd_out; +reg [COLOR_DEPTH*3-1:0] sd_out; // ================================================================== // ======================== the line buffers ======================== // ================================================================== // 2 lines of 2**HCNT_WIDTH pixels 3*COLOR_DEPTH bit RGB -(* ramstyle = "no_rw_check" *) reg [3+COLOR_DEPTH*3-1:0] sd_buffer[2*2**HCNT_WIDTH]; +(* ramstyle = "no_rw_check" *) reg [COLOR_DEPTH*3-1:0] sd_buffer[2*2**HCNT_WIDTH]; // use alternating sd_buffers when storing/reading data reg line_toggle; @@ -165,6 +159,10 @@ reg line_toggle; reg [HCNT_WIDTH-1:0] hcnt; reg [HSCNT_WIDTH:0] hs_max; reg [HSCNT_WIDTH:0] hs_rise; +reg [HCNT_WIDTH:0] hb_fall[2]; +reg [HCNT_WIDTH:0] hb_rise[2]; +reg [HCNT_WIDTH+1:0] vb_event[2]; +reg [HCNT_WIDTH+1:0] vs_event[2]; reg [HSCNT_WIDTH:0] synccnt; // Input pixel clock, aligned with input sync: @@ -178,12 +176,21 @@ wire ce_x1 = (i_div == ce_divider_in); always @(posedge clk_sys) begin reg hsD, vsD; reg vbD; + reg hbD; // Pixel logic on x1 clkena if(ce_x1) begin hcnt <= hcnt + 1'd1; + vsD <= vs_in; vbD <= vb_in; - sd_buffer[{line_toggle, hcnt}] <= {vbD & ~vb_in, ~vbD & vb_in, hb_in, r_in, g_in, b_in}; + + sd_buffer[{line_toggle, hcnt}] <= {r_in, g_in, b_in}; + if (vbD ^ vb_in) vb_event[line_toggle] <= {1'b1, vb_in, hcnt}; + if (vsD ^ vs_in) vs_event[line_toggle] <= {1'b1, vs_in, hcnt}; + // save position of hblank + hbD <= hb_in; + if(!hbD && hb_in) hb_rise[line_toggle] <= {1'b1, hcnt}; + if( hbD && !hb_in) hb_fall[line_toggle] <= {1'b1, hcnt}; end // Generate pixel clock @@ -209,10 +216,13 @@ always @(posedge clk_sys) begin 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; + if(hsD && !hs_in) begin + line_toggle <= !line_toggle; + vb_event[!line_toggle] <= 0; + vs_event[!line_toggle] <= 0; + hb_rise[!line_toggle][HCNT_WIDTH] <= 0; + hb_fall[!line_toggle][HCNT_WIDTH] <= 0; + end end @@ -223,10 +233,9 @@ end reg [HSCNT_WIDTH:0] sd_synccnt; reg [HCNT_WIDTH-1:0] sd_hcnt; reg vb_sd = 0; -wire vb_on = sd_out[COLOR_DEPTH*3+1]; -wire vb_off = sd_out[COLOR_DEPTH*3+2]; reg hb_sd = 0; reg hs_sd = 0; +reg vs_sd = 0; // Output pixel clock, aligned with output sync: reg [2:0] sd_i_div; @@ -244,11 +253,18 @@ always @(posedge clk_sys) begin // read data from line sd_buffer sd_out <= sd_buffer[{~line_toggle, sd_hcnt}]; - if (vb_on) vb_sd <= 1; - if (vb_off) vb_sd <= 0; - hb_sd <= sd_out[COLOR_DEPTH*3]; + // Handle VBlank event + if(vb_event[~line_toggle][HCNT_WIDTH+1] && sd_hcnt == vb_event[~line_toggle][HCNT_WIDTH-1:0]) vb_sd <= vb_event[~line_toggle][HCNT_WIDTH]; + // Handle VSync event + if(vs_event[~line_toggle][HCNT_WIDTH+1] && sd_hcnt == vs_event[~line_toggle][HCNT_WIDTH-1:0]) vs_sd <= vs_event[~line_toggle][HCNT_WIDTH]; + // Handle HBlank events + if(hb_rise[~line_toggle][HCNT_WIDTH] && sd_hcnt == hb_rise[~line_toggle][HCNT_WIDTH-1:0]) hb_sd <= 1; + if(hb_fall[~line_toggle][HCNT_WIDTH] && sd_hcnt == hb_fall[~line_toggle][HCNT_WIDTH-1:0]) hb_sd <= 0; end + sd_i_div <= sd_i_div + 1'd1; + if (sd_i_div==ce_divider_adj) sd_i_div <= 3'b000; + // Framing logic on sysclk sd_synccnt <= sd_synccnt + 1'd1; hsD <= hs_in; @@ -256,13 +272,6 @@ always @(posedge clk_sys) begin if(sd_synccnt == hs_max || (hsD && !hs_in)) 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 @@ -271,4 +280,10 @@ always @(posedge clk_sys) begin end +wire ce_x4 = sd_i_div[0]; // Faster pixel_ena for higher subdivisions to prevent blending from becoming to coarse. + +assign pixel_ena = ce_divider_out > 3'd5 ? + bypass ? ce_x2 : ce_x4 : + bypass ? ce_x1 : ce_x2 ; + endmodule diff --git a/common/mist/spdif.v b/common/mist/spdif.v new file mode 100644 index 00000000..2c9ce78f --- /dev/null +++ b/common/mist/spdif.v @@ -0,0 +1,337 @@ +//----------------------------------------------------------------- +// SPDIF Transmitter +// V0.1 +// Ultra-Embedded.com +// Copyright 2012 +// +// Email: admin@ultra-embedded.com +// +// License: GPL +// If you would like a version with a more permissive license for +// use in closed source commercial applications please contact me +// for details. +//----------------------------------------------------------------- +// +// This file is open source HDL; 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 2 of +// the License, or (at your option) any later version. +// +// This 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 file; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA +//----------------------------------------------------------------- +// altera message_off 10762 +// altera message_off 10240 + +module spdif +( + input clk_i, + input rst_i, + + input [31:0] clk_rate_i, + + // Output + output spdif_o, + + // Audio interface (16-bit x 2 = RL) + input [31:0] sample_i, + output reg sample_req_o +); + +// SPDIF bit output enable +// Single cycle pulse synchronous to clk_i which drives +// the output bit rate. +// For 44.1KHz, 44100×32×2×2 = 5,644,800Hz +// For 48KHz, 48000×32×2×2 = 6,144,000Hz + +parameter bit_clk = 48000*32*2*2; + +reg bit_out_en_i; + +reg [31:0] cnt; +wire [31:0] cnt_next = cnt + bit_clk; + +always @(posedge clk_i) begin + bit_out_en_i <= 0; + cnt <= cnt_next; + if(cnt_next >= clk_rate_i) begin + cnt <= cnt_next - clk_rate_i; + bit_out_en_i <= 1; + end +end + +//----------------------------------------------------------------- +// Registers +//----------------------------------------------------------------- +reg [15:0] audio_sample_q; +reg [8:0] subframe_count_q; + +reg load_subframe_q; +reg [7:0] preamble_q; +wire [31:0] subframe_w; + +reg [5:0] bit_count_q; +reg bit_toggle_q; + +reg spdif_out_q; + +reg [5:0] parity_count_q; + +reg channel_status_bit_q; + +//----------------------------------------------------------------- +// Subframe Counter +//----------------------------------------------------------------- +always @ (posedge rst_i or posedge clk_i ) +begin + if (rst_i == 1'b1) + subframe_count_q <= 9'd0; + else if (load_subframe_q) + begin + // 192 frames (384 subframes) in an audio block + if (subframe_count_q == 9'd383) + subframe_count_q <= 9'd0; + else + subframe_count_q <= subframe_count_q + 9'd1; + end +end + +//----------------------------------------------------------------- +// Sample capture +//----------------------------------------------------------------- +reg [15:0] sample_buf_q; + +always @ (posedge rst_i or posedge clk_i ) +begin + if (rst_i == 1'b1) + begin + audio_sample_q <= 16'h0000; + sample_buf_q <= 16'h0000; + sample_req_o <= 1'b0; + end + else if (load_subframe_q) + begin + // Start of frame (first subframe)? + if (subframe_count_q[0] == 1'b0) + begin + // Use left sample + audio_sample_q <= sample_i[15:0]; + + // Store right sample + sample_buf_q <= sample_i[31:16]; + + // Request next sample + sample_req_o <= 1'b1; + end + else + begin + // Use right sample + audio_sample_q <= sample_buf_q; + + sample_req_o <= 1'b0; + end + end + else + sample_req_o <= 1'b0; +end + +// Timeslots 3 - 0 = Preamble +assign subframe_w[3:0] = 4'b0000; + +// Timeslots 7 - 4 = 24-bit audio LSB +assign subframe_w[7:4] = 4'b0000; + +// Timeslots 11 - 8 = 20-bit audio LSB +assign subframe_w[11:8] = 4'b0000; + +// Timeslots 27 - 12 = 16-bit audio +assign subframe_w[27:12] = audio_sample_q; + +// Timeslots 28 = Validity +assign subframe_w[28] = 1'b0; // Valid + +// Timeslots 29 = User bit +assign subframe_w[29] = 1'b0; + +// Timeslots 30 = Channel status bit +assign subframe_w[30] = channel_status_bit_q ; //was constant 1'b0 enabling copy-bit; + +// Timeslots 31 = Even Parity bit (31:4) +assign subframe_w[31] = 1'b0; + +//----------------------------------------------------------------- +// Preamble and Channel status bit +//----------------------------------------------------------------- +localparam PREAMBLE_Z = 8'b00010111; // "B" channel A data at start of block +localparam PREAMBLE_Y = 8'b00100111; // "W" channel B data +localparam PREAMBLE_X = 8'b01000111; // "M" channel A data not at start of block + +reg [7:0] preamble_r; +reg channel_status_bit_r; + +always @ * +begin + // Start of audio block? + // Z(B) - Left channel + if (subframe_count_q == 9'd0) + preamble_r = PREAMBLE_Z; // Z(B) + // Right Channel? + else if (subframe_count_q[0] == 1'b1) + preamble_r = PREAMBLE_Y; // Y(W) + // Left Channel (but not start of block)? + else + preamble_r = PREAMBLE_X; // X(M) + + if (subframe_count_q[8:1] == 8'd2) // frame 2 => subframes 4 and 5 => 0 = copy inhibited, 1 = copy permitted + channel_status_bit_r = 1'b1; + else if (subframe_count_q[8:1] == 8'd15) // frame 15 => 0 = no indication, 1 = original media + channel_status_bit_r = 1'b1; + else if (subframe_count_q[8:1] == 8'd25) // frame 24 to 27 => sample frequency, 0100 = 48kHz, 0000 = 44kHz (l2r) + channel_status_bit_r = 1'b1; + else + channel_status_bit_r = 1'b0; // everything else defaults to 0 +end + +always @ (posedge rst_i or posedge clk_i ) +begin + if (rst_i == 1'b1) + begin + preamble_q <= 8'h00; + channel_status_bit_q <= 1'b0; + end + else if (load_subframe_q) + begin + preamble_q <= preamble_r; + channel_status_bit_q <= channel_status_bit_r; + end +end + +//----------------------------------------------------------------- +// Parity Counter +//----------------------------------------------------------------- +always @ (posedge rst_i or posedge clk_i ) +begin + if (rst_i == 1'b1) + begin + parity_count_q <= 6'd0; + end + // Time to output a bit? + else if (bit_out_en_i) + begin + // Preamble bits? + if (bit_count_q < 6'd8) + begin + parity_count_q <= 6'd0; + end + // Normal timeslots + else if (bit_count_q < 6'd62) + begin + // On first pass through this timeslot, count number of high bits + if (bit_count_q[0] == 0 && subframe_w[bit_count_q / 2] == 1'b1) + parity_count_q <= parity_count_q + 6'd1; + end + end +end + +//----------------------------------------------------------------- +// Bit Counter +//----------------------------------------------------------------- +always @ (posedge rst_i or posedge clk_i) +begin + if (rst_i == 1'b1) + begin + bit_count_q <= 6'b0; + load_subframe_q <= 1'b1; + end + // Time to output a bit? + else if (bit_out_en_i) + begin + // 32 timeslots (x2 for double frequency) + if (bit_count_q == 6'd63) + begin + bit_count_q <= 6'd0; + load_subframe_q <= 1'b1; + end + else + begin + bit_count_q <= bit_count_q + 6'd1; + load_subframe_q <= 1'b0; + end + end + else + load_subframe_q <= 1'b0; +end + +//----------------------------------------------------------------- +// Bit half toggle +//----------------------------------------------------------------- +always @ (posedge rst_i or posedge clk_i) +if (rst_i == 1'b1) + bit_toggle_q <= 1'b0; +// Time to output a bit? +else if (bit_out_en_i) + bit_toggle_q <= ~bit_toggle_q; + +//----------------------------------------------------------------- +// Output bit (BMC encoded) +//----------------------------------------------------------------- +reg bit_r; + +always @ * +begin + bit_r = spdif_out_q; + + // Time to output a bit? + if (bit_out_en_i) + begin + // Preamble bits? + if (bit_count_q < 6'd8) + begin + bit_r = preamble_q[bit_count_q[2:0]]; + end + // Normal timeslots + else if (bit_count_q < 6'd62) + begin + if (subframe_w[bit_count_q / 2] == 1'b0) + begin + if (bit_toggle_q == 1'b0) + bit_r = ~spdif_out_q; + else + bit_r = spdif_out_q; + end + else + bit_r = ~spdif_out_q; + end + // Parity timeslot + else + begin + // Even number of high bits, make odd + if (parity_count_q[0] == 1'b0) + begin + if (bit_toggle_q == 1'b0) + bit_r = ~spdif_out_q; + else + bit_r = spdif_out_q; + end + else + bit_r = ~spdif_out_q; + end + end +end + +always @ (posedge rst_i or posedge clk_i ) +if (rst_i == 1'b1) + spdif_out_q <= 1'b0; +else + spdif_out_q <= bit_r; + +assign spdif_o = spdif_out_q; + +endmodule diff --git a/common/mist/user_io.v b/common/mist/user_io.v index 80bf1a87..89bd3da7 100644 --- a/common/mist/user_io.v +++ b/common/mist/user_io.v @@ -24,7 +24,7 @@ module user_io ( input [(8*STRLEN)-1:0] conf_str, - output [9:0] conf_addr, // RAM address for config string, if STRLEN=0 + output [10:0] conf_addr, // RAM address for config string, if STRLEN=0 input [7:0] conf_chr, input clk_sys, // clock for system-related messages (kbd, joy, etc...) @@ -66,7 +66,7 @@ module user_io ( output reg sd_dout_strobe = 0, input [7:0] sd_din, output reg sd_din_strobe = 0, - output reg [8:0] sd_buff_addr, + output reg [8+SD_BLKSZ:0] sd_buff_addr, output reg [SD_IMAGES-1:0] img_mounted, // rising edge if a new image is mounted output reg [63:0] img_size, // size of image in bytes @@ -98,6 +98,16 @@ module user_io ( output reg mouse_strobe, // mouse data is valid on mouse_strobe output reg mouse_idx, // which mouse? + // i2c bridge + output reg i2c_start, + output reg i2c_read, + output reg [6:0] i2c_addr, + output reg [7:0] i2c_subaddr, + output reg [7:0] i2c_dout, + input [7:0] i2c_din, + input i2c_ack, + input i2c_end, + // serial com port input [7:0] serial_data, input serial_strobe @@ -110,13 +120,14 @@ parameter SD_IMAGES=2; // number of block-access images (max. 4 supported in cur parameter PS2BIDIR=0; // bi-directional PS2 interface parameter FEATURES=0; // requested features from the firmware parameter ARCHIE=0; +parameter SD_BLKSZ=1'b0; // blocksize = 512< neg; +end + +endmodule