mirror of
https://github.com/MiSTer-devel/MacPlus_MiSTer.git
synced 2025-02-20 05:29:03 +00:00
396 lines
9.6 KiB
Systemverilog
396 lines
9.6 KiB
Systemverilog
//============================================================================
|
|
//
|
|
// HDMI Lite output module
|
|
// Copyright (C) 2017 Sorgelig
|
|
//
|
|
// This program 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 2 of the License, or (at your option)
|
|
// any later version.
|
|
//
|
|
//============================================================================
|
|
|
|
|
|
module hdmi_lite
|
|
(
|
|
input reset,
|
|
|
|
input clk_video,
|
|
input ce_pixel,
|
|
input video_vs,
|
|
input video_de,
|
|
input [23:0] video_d,
|
|
|
|
input clk_hdmi,
|
|
input hdmi_hde,
|
|
input hdmi_vde,
|
|
output reg hdmi_de,
|
|
output [23:0] hdmi_d,
|
|
|
|
input [11:0] screen_w,
|
|
input [11:0] screen_h,
|
|
input quadbuf,
|
|
|
|
// 0-3 => scale 1-4
|
|
input [1:0] scale_x,
|
|
input [1:0] scale_y,
|
|
input scale_auto,
|
|
|
|
input clk_vbuf,
|
|
output [27:0] vbuf_address,
|
|
input [127:0] vbuf_readdata,
|
|
output [127:0] vbuf_writedata,
|
|
output [7:0] vbuf_burstcount,
|
|
output [15:0] vbuf_byteenable,
|
|
input vbuf_waitrequest,
|
|
input vbuf_readdatavalid,
|
|
output reg vbuf_read,
|
|
output reg vbuf_write
|
|
);
|
|
|
|
localparam [7:0] burstsz = 64;
|
|
|
|
reg [1:0] nbuf = 0;
|
|
wire [27:0] read_buf = {4'd2, 3'b000, (quadbuf ? nbuf-2'd1 : 2'b00), 19'd0};
|
|
wire [27:0] write_buf = {4'd2, 3'b000, (quadbuf ? nbuf+2'd1 : 2'b00), 19'd0};
|
|
|
|
assign vbuf_address = vbuf_write ? vbuf_waddress : vbuf_raddress;
|
|
assign vbuf_burstcount = vbuf_write ? vbuf_wburstcount : vbuf_rburstcount;
|
|
|
|
wire [95:0] hf_out;
|
|
wire [7:0] hf_usedw;
|
|
reg hf_reset = 0;
|
|
|
|
vbuf_fifo out_fifo
|
|
(
|
|
.aclr(hf_reset),
|
|
|
|
.wrclk(clk_vbuf),
|
|
.wrreq(vbuf_readdatavalid),
|
|
.data({vbuf_readdata[96+:24],vbuf_readdata[64+:24],vbuf_readdata[32+:24],vbuf_readdata[0+:24]}),
|
|
.wrusedw(hf_usedw),
|
|
|
|
.rdclk(~clk_hdmi),
|
|
.rdreq(hf_rdreq),
|
|
.q(hf_out)
|
|
);
|
|
|
|
reg [11:0] rd_stride;
|
|
wire [7:0] rd_burst = (burstsz < rd_stride) ? burstsz : rd_stride[7:0];
|
|
|
|
reg [27:0] vbuf_raddress;
|
|
reg [7:0] vbuf_rburstcount;
|
|
always @(posedge clk_vbuf) begin
|
|
reg [18:0] rdcnt;
|
|
reg [7:0] bcnt;
|
|
reg vde1, vde2;
|
|
reg [1:0] mcnt;
|
|
reg [1:0] my;
|
|
reg [18:0] fsz;
|
|
reg [11:0] strd;
|
|
|
|
vde1 <= hdmi_vde;
|
|
vde2 <= vde1;
|
|
|
|
if(vbuf_readdatavalid) begin
|
|
rdcnt <= rdcnt + 1'd1;
|
|
if(bcnt) bcnt <= bcnt - 1'd1;
|
|
vbuf_raddress <= vbuf_raddress + 1'd1;
|
|
end
|
|
|
|
if(!bcnt && reading) reading <= 0;
|
|
|
|
vbuf_read <= 0;
|
|
if(~vbuf_waitrequest) begin
|
|
if(!hf_reset && rdcnt<fsz && !bcnt && hf_usedw < burstsz && allow_rd) begin
|
|
vbuf_read <= 1;
|
|
reading <= 1;
|
|
bcnt <= rd_burst;
|
|
vbuf_rburstcount <= rd_burst;
|
|
rd_stride <= rd_stride - rd_burst;
|
|
if(!(rd_stride - rd_burst)) rd_stride <= strd;
|
|
|
|
if(!rdcnt) begin
|
|
vbuf_raddress <= read_buf;
|
|
mcnt <= my;
|
|
end
|
|
else if (rd_stride == strd) begin
|
|
mcnt <= mcnt - 1'd1;
|
|
if(!mcnt) mcnt <= my;
|
|
else vbuf_raddress <= vbuf_raddress - strd;
|
|
end
|
|
end
|
|
end
|
|
|
|
hf_reset <= 0;
|
|
if(vde2 & ~vde1) begin
|
|
hf_reset <= 1;
|
|
rdcnt <= 0;
|
|
bcnt <= 0;
|
|
rd_stride <= stride;
|
|
strd <= stride;
|
|
fsz <= framesz;
|
|
my <= mult_y;
|
|
end
|
|
end
|
|
|
|
|
|
reg [11:0] off_x, off_y;
|
|
reg [11:0] x, y;
|
|
reg [11:0] vh_height;
|
|
reg [11:0] vh_width;
|
|
reg [1:0] pcnt;
|
|
reg [1:0] hload;
|
|
wire hf_rdreq = (x>=off_x) && (x<(vh_width+off_x)) && (y>=off_y) && (y<(vh_height+off_y)) && !hload && !pcnt;
|
|
wire de_in = hdmi_hde & hdmi_vde;
|
|
|
|
always @(posedge clk_hdmi) begin
|
|
reg [71:0] px_out;
|
|
reg [1:0] mx;
|
|
reg vde;
|
|
|
|
vde <= hdmi_vde;
|
|
|
|
if(vde & ~hdmi_vde) begin
|
|
off_x <= (screen_w>v_width) ? (screen_w - v_width)>>1 : 12'd0;
|
|
off_y <= (screen_h>v_height) ? (screen_h - v_height)>>1 : 12'd0;
|
|
vh_height <= v_height;
|
|
vh_width <= v_width;
|
|
mx <= mult_x;
|
|
end
|
|
|
|
pcnt <= pcnt + 1'd1;
|
|
if(pcnt == mx) begin
|
|
pcnt <= 0;
|
|
hload <= hload + 1'd1;
|
|
end
|
|
|
|
if(~de_in || x<off_x || y<off_y) begin
|
|
hload <= 0;
|
|
pcnt <= 0;
|
|
end
|
|
|
|
hdmi_de <= de_in;
|
|
|
|
x <= x + 1'd1;
|
|
if(~hdmi_de & de_in) x <= 0;
|
|
if(hdmi_de & ~de_in) y <= y + 1'd1;
|
|
if(~hdmi_vde) y <= 0;
|
|
|
|
if(!pcnt) {px_out, hdmi_d} <= {24'd0, px_out};
|
|
if(hf_rdreq) {px_out, hdmi_d} <= hf_out;
|
|
end
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
reg reading = 0;
|
|
reg writing = 0;
|
|
|
|
reg op_split = 0;
|
|
always @(posedge clk_vbuf) op_split <= ~op_split;
|
|
|
|
wire allow_rd = ~reading & ~writing & op_split & ~reset;
|
|
wire allow_wr = ~reading & ~writing & ~op_split & ~reset;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
reg vf_rdreq = 0;
|
|
wire [95:0] vf_out;
|
|
assign vbuf_writedata = {8'h00, vf_out[95:72], 8'h00, vf_out[71:48], 8'h00, vf_out[47:24], 8'h00, vf_out[23:0]};
|
|
|
|
vbuf_fifo in_fifo
|
|
(
|
|
.aclr(video_vs),
|
|
|
|
.rdclk(clk_vbuf),
|
|
.rdreq(vf_rdreq & ~vbuf_waitrequest),
|
|
.q(vf_out),
|
|
|
|
.wrclk(clk_video),
|
|
.wrreq(infifo_wr),
|
|
.data({video_de ? video_d : 24'd0, pix_acc})
|
|
);
|
|
|
|
assign vbuf_byteenable = '1;
|
|
|
|
reg [35:0] addrque[3:0] = '{0,0,0,0};
|
|
|
|
reg [7:0] flush_size;
|
|
reg [27:0] flush_addr;
|
|
reg flush_req = 0;
|
|
reg flush_ack = 0;
|
|
|
|
reg [27:0] vbuf_waddress;
|
|
reg [7:0] vbuf_wburstcount;
|
|
|
|
always @(posedge clk_vbuf) begin
|
|
reg [7:0] ibcnt = 0;
|
|
reg reqd = 0;
|
|
|
|
reqd <= flush_req;
|
|
|
|
if(~vbuf_waitrequest) begin
|
|
vbuf_write <= vf_rdreq;
|
|
if(~vf_rdreq && writing) writing <= 0;
|
|
if(!vf_rdreq && !vbuf_write && addrque[0] && allow_wr) begin
|
|
{vbuf_waddress, vbuf_wburstcount} <= addrque[0];
|
|
ibcnt <= addrque[0][7:0];
|
|
addrque[0] <= addrque[1];
|
|
addrque[1] <= addrque[2];
|
|
addrque[2] <= addrque[3];
|
|
addrque[3] <= 0;
|
|
vf_rdreq <= 1;
|
|
writing <= 1;
|
|
end
|
|
else if(flush_ack != reqd) begin
|
|
if(!addrque[0]) addrque[0] <= {flush_addr, flush_size};
|
|
else if(!addrque[1]) addrque[1] <= {flush_addr, flush_size};
|
|
else if(!addrque[2]) addrque[2] <= {flush_addr, flush_size};
|
|
else if(!addrque[3]) addrque[3] <= {flush_addr, flush_size};
|
|
flush_ack <= reqd;
|
|
end
|
|
|
|
if(vf_rdreq) begin
|
|
if(ibcnt == 1) vf_rdreq <= 0;
|
|
ibcnt <= ibcnt - 1'd1;
|
|
end
|
|
end
|
|
end
|
|
|
|
reg [11:0] stride;
|
|
reg [18:0] framesz;
|
|
reg [11:0] v_height;
|
|
reg [11:0] v_width;
|
|
reg [1:0] mult_x;
|
|
reg [1:0] mult_y;
|
|
|
|
reg [71:0] pix_acc;
|
|
wire pix_wr = ce_pixel && video_de;
|
|
|
|
reg [27:0] cur_addr;
|
|
reg [11:0] video_x;
|
|
reg [11:0] video_y;
|
|
|
|
wire infifo_tail = ~video_de && video_x[1:0];
|
|
wire infifo_wr = (pix_wr && &video_x[1:0]) || infifo_tail;
|
|
|
|
wire [1:0] tm_y = (video_y > (screen_h/2)) ? 2'b00 : (video_y > (screen_h/3)) ? 2'b01 : (video_y > (screen_h/4)) ? 2'b10 : 2'b11;
|
|
wire [1:0] tm_x = (l1_width > (screen_w/2)) ? 2'b00 : (l1_width > (screen_w/3)) ? 2'b01 : (l1_width > (screen_w/4)) ? 2'b10 : 2'b11;
|
|
wire [1:0] tm_xy = (tm_x < tm_y) ? tm_x : tm_y;
|
|
wire [1:0] tmf_y = scale_auto ? tm_xy : scale_y;
|
|
wire [1:0] tmf_x = scale_auto ? tm_xy : scale_x;
|
|
wire [11:0] t_height = video_y + (tmf_y[0] ? video_y : 12'd0) + (tmf_y[1] ? video_y<<1 : 12'd0);
|
|
wire [11:0] t_width = l1_width + (tmf_x[0] ? l1_width : 12'd0) + (tmf_x[1] ? l1_width<<1 : 12'd0);
|
|
wire [23:0] t_fsz = l1_stride * t_height;
|
|
|
|
reg [11:0] l1_width;
|
|
reg [11:0] l1_stride;
|
|
always @(posedge clk_video) begin
|
|
reg [7:0] loaded = 0;
|
|
reg [11:0] strd = 0;
|
|
reg old_de = 0;
|
|
reg old_vs = 0;
|
|
|
|
old_vs <= video_vs;
|
|
if(~old_vs & video_vs) begin
|
|
cur_addr<= write_buf;
|
|
video_x <= 0;
|
|
video_y <= 0;
|
|
loaded <= 0;
|
|
strd <= 0;
|
|
nbuf <= nbuf + 1'd1;
|
|
|
|
stride <= l1_stride;
|
|
framesz <= t_fsz[18:0];
|
|
v_height<= t_height;
|
|
v_width <= t_width;
|
|
mult_x <= tmf_x;
|
|
mult_y <= tmf_y;
|
|
end
|
|
|
|
if(pix_wr) begin
|
|
case(video_x[1:0])
|
|
0: pix_acc <= video_d; // zeroes upper bits too
|
|
1: pix_acc[47:24] <= video_d;
|
|
2: pix_acc[71:48] <= video_d;
|
|
3: loaded <= loaded + 1'd1;
|
|
endcase
|
|
if(video_x<screen_w) video_x <= video_x + 1'd1;
|
|
end
|
|
|
|
old_de <= video_de;
|
|
if((!video_x[1:0] && loaded >= burstsz) || (old_de & ~video_de)) begin
|
|
if(loaded + infifo_tail) begin
|
|
flush_size <= loaded + infifo_tail;
|
|
flush_addr <= cur_addr;
|
|
flush_req <= ~flush_req;
|
|
loaded <= 0;
|
|
strd <= strd + loaded;
|
|
end
|
|
|
|
cur_addr <= cur_addr + loaded + infifo_tail;
|
|
if(~video_de) begin
|
|
if(video_y<screen_h) video_y <= video_y + 1'd1;
|
|
video_x <= 0;
|
|
strd <= 0;
|
|
|
|
// measure width by first line (same as VIP)
|
|
if(!video_y) begin
|
|
l1_width <= video_x;
|
|
l1_stride <= strd + loaded + infifo_tail;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|
|
|
|
module vbuf_fifo
|
|
(
|
|
input aclr,
|
|
|
|
input rdclk,
|
|
input rdreq,
|
|
output [95:0] q,
|
|
|
|
input wrclk,
|
|
input wrreq,
|
|
input [95:0] data,
|
|
output [7:0] wrusedw
|
|
);
|
|
|
|
dcfifo dcfifo_component
|
|
(
|
|
.aclr (aclr),
|
|
.data (data),
|
|
.rdclk (rdclk),
|
|
.rdreq (rdreq),
|
|
.wrclk (wrclk),
|
|
.wrreq (wrreq),
|
|
.q (q),
|
|
.wrusedw (wrusedw),
|
|
.eccstatus (),
|
|
.rdempty (),
|
|
.rdfull (),
|
|
.rdusedw (),
|
|
.wrempty (),
|
|
.wrfull ()
|
|
);
|
|
|
|
defparam
|
|
dcfifo_component.intended_device_family = "Cyclone V",
|
|
dcfifo_component.lpm_numwords = 256,
|
|
dcfifo_component.lpm_showahead = "OFF",
|
|
dcfifo_component.lpm_type = "dcfifo",
|
|
dcfifo_component.lpm_width = 96,
|
|
dcfifo_component.lpm_widthu = 8,
|
|
dcfifo_component.overflow_checking = "ON",
|
|
dcfifo_component.rdsync_delaypipe = 5,
|
|
dcfifo_component.read_aclr_synch = "OFF",
|
|
dcfifo_component.underflow_checking = "ON",
|
|
dcfifo_component.use_eab = "ON",
|
|
dcfifo_component.write_aclr_synch = "OFF",
|
|
dcfifo_component.wrsync_delaypipe = 5;
|
|
|
|
endmodule
|