1
0
mirror of https://github.com/marqs85/ossc.git synced 2024-11-11 12:09:07 +00:00
ossc/rtl/linebuf_top.v
2023-02-28 19:39:59 +02:00

279 lines
9.5 KiB
Verilog

//
// Copyright (C) 2022 Markus Hiienkari <mhiienka@niksula.hut.fi>
//
// This file is part of Open Source Scan Converter project.
//
// 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 3 of the License, or
// (at your option) any later version.
//
// This program 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/>.
//
//`define DEBUG
module linebuf_top (
input PCLK_CAP_i,
input PCLK_OUT_i,
input [7:0] R_i,
input [7:0] G_i,
input [7:0] B_i,
input DE_i,
input datavalid_i,
input [11:0] h_in_active,
input [10:0] xpos_i,
input [10:0] ypos_i,
input [10:0] xpos_lb,
input [10:0] ypos_lb,
input [10:0] ypos_lb_next,
input line_id,
input lb_enable,
output [7:0] R_linebuf,
output [7:0] G_linebuf,
output [7:0] B_linebuf,
// optional EMIF if
input emif_br_clk,
input emif_br_reset,
output reg [27:0] emif_rd_addr,
output reg emif_rd_read,
input [255:0] emif_rd_rdata,
input emif_rd_waitrequest,
input emif_rd_readdatavalid,
output reg [5:0] emif_rd_burstcount,
output [27:0] emif_wr_addr,
output reg emif_wr_write,
output [255:0] emif_wr_wdata,
input emif_wr_waitrequest,
output reg [5:0] emif_wr_burstcount
);
parameter EMIF_ENABLE = 0;
parameter NUM_LINE_BUFFERS = 32;
localparam EMIF_WR_MAXBURST = 32;
localparam EMIF_RD_MAXBURST = 32;
generate
if (EMIF_ENABLE) begin
`ifdef DEBUG
reg [10:0] emif_wr_stall_ctr /* synthesis noprune */;
reg [10:0] emif_rd_stall_ctr /* synthesis noprune */;
reg emif_rd_line_missed /* synthesis noprune */;
always @(posedge emif_br_clk) begin
if (emif_wr_write & emif_wr_waitrequest & (emif_wr_stall_ctr != 2047))
emif_wr_stall_ctr <= emif_wr_stall_ctr + 1'b1;
else
emif_wr_stall_ctr <= 0;
if (emif_rd_burstcount > 0) begin
if (emif_rd_readdatavalid)
emif_rd_stall_ctr <= 0;
else
emif_rd_stall_ctr <= emif_rd_stall_ctr + 1'b1;
end
end
`endif
// Number of 8-pixel blocks per line
wire [8:0] blocks_per_line = (h_in_active / 8) + (h_in_active[2:0] != 3'h0);
/* ------------------------------ EMIF WR interface ------------------------------ */
// PCLK_CAP_i related signals
reg [8:0] emif_wr_fifo_blksleft;
reg [19:0] emif_wr_fifo_addr; // [19:9] = y_pos; [8:0] = x_pos_x8
reg [23:0] emif_wr_fifo_pixs[7:0] /* synthesis ramstyle = "logic" */;
reg emif_wr_fifo_wrreq;
reg [8:0] blocks_inserted;
reg DE_prev;
wire emif_wr_fifo_wrfull;
// emif_br_clk related signals
wire [8:0] emif_wr_fifo_blksleft_q;
reg [8:0] emif_wr_blksleft;
wire [19:0] emif_wr_fifo_addr_q;
wire [23:0] emif_wr_fifo_pixs_q[7:0];
reg emif_wr_fifo_iniread_prev;
wire emif_wr_fifo_rdempty;
wire [8:0] emif_wr_fifo_rdusedw;
wire emif_wr_fifo_iniread = (lb_enable & (emif_wr_blksleft == 0) & !emif_wr_fifo_iniread_prev & !emif_wr_fifo_rdempty);
wire emif_wr_fifo_rdreq = emif_wr_fifo_iniread | ((emif_wr_burstcount > 1) & !emif_wr_waitrequest);
assign emif_wr_addr = {3'b001, emif_wr_fifo_addr_q, 5'h0};
assign emif_wr_wdata = {8'h0, emif_wr_fifo_pixs_q[0], 8'h0, emif_wr_fifo_pixs_q[1], 8'h0, emif_wr_fifo_pixs_q[2], 8'h0, emif_wr_fifo_pixs_q[3],
8'h0, emif_wr_fifo_pixs_q[4], 8'h0, emif_wr_fifo_pixs_q[5], 8'h0, emif_wr_fifo_pixs_q[6], 8'h0, emif_wr_fifo_pixs_q[7]};
dc_fifo_emif_wr dc_fifo_emif_wr_inst (
.data({emif_wr_fifo_blksleft, emif_wr_fifo_addr,
emif_wr_fifo_pixs[0], emif_wr_fifo_pixs[1], emif_wr_fifo_pixs[2], emif_wr_fifo_pixs[3],
emif_wr_fifo_pixs[4], emif_wr_fifo_pixs[5], emif_wr_fifo_pixs[6], emif_wr_fifo_pixs[7]}),
.rdclk(emif_br_clk),
.rdreq(emif_wr_fifo_rdreq),
.rdempty(emif_wr_fifo_rdempty),
.rdusedw(emif_wr_fifo_rdusedw),
.wrclk(PCLK_CAP_i),
.wrreq(emif_wr_fifo_wrreq),
.wrfull(emif_wr_fifo_wrfull),
.q({emif_wr_fifo_blksleft_q, emif_wr_fifo_addr_q,
emif_wr_fifo_pixs_q[0], emif_wr_fifo_pixs_q[1], emif_wr_fifo_pixs_q[2], emif_wr_fifo_pixs_q[3],
emif_wr_fifo_pixs_q[4], emif_wr_fifo_pixs_q[5], emif_wr_fifo_pixs_q[6], emif_wr_fifo_pixs_q[7]})
);
always @(posedge PCLK_CAP_i) begin
emif_wr_fifo_wrreq <= 1'b0;
if (datavalid_i) begin
emif_wr_fifo_pixs[xpos_i[2:0]] <= {R_i, G_i, B_i};
DE_prev <= DE_i;
if (~DE_prev & DE_i)
blocks_inserted <= 0;
if ((blocks_inserted < blocks_per_line) & ~emif_wr_fifo_wrfull & (xpos_i[2:0] == 3'h7)) begin
emif_wr_fifo_blksleft <= blocks_per_line - blocks_inserted;
emif_wr_fifo_addr[19:9] <= ypos_i;
emif_wr_fifo_addr[7:0] <= xpos_i[10:3];
emif_wr_fifo_wrreq <= 1'b1;
blocks_inserted <= blocks_inserted + 1'b1;
end
end
end
always @(posedge emif_br_clk or posedge emif_br_reset) begin
if (emif_br_reset) begin
emif_wr_write <= 1'b0;
emif_wr_burstcount <= 1'b0;
end else begin
if (emif_wr_burstcount > 0) begin
if (!emif_wr_waitrequest) begin
emif_wr_burstcount <= emif_wr_burstcount - 1'b1;
if (emif_wr_burstcount == 1) begin
emif_wr_blksleft <= 0;
emif_wr_write <= 1'b0;
end
end
end else if (lb_enable & (emif_wr_blksleft > 0) & ((emif_wr_fifo_rdusedw >= emif_wr_blksleft-1) | (emif_wr_fifo_rdusedw >= 31))) begin
emif_wr_write <= 1'b1;
emif_wr_burstcount <= (emif_wr_blksleft > EMIF_WR_MAXBURST) ? EMIF_WR_MAXBURST : emif_wr_blksleft;
end else if (emif_wr_fifo_iniread_prev) begin
emif_wr_blksleft <= emif_wr_fifo_blksleft_q;
end
emif_wr_fifo_iniread_prev <= emif_wr_fifo_iniread;
end
end
/* ------------------------------ EMIF RD interface ------------------------------ */
// PCLK_OUT_i related signals
wire [12:0] linebuf_rdaddr = {xpos_lb + (line_id ? 2560 : 0)};
// emif_br_clk related signals
reg [8:0] blocks_copied;
wire linebuf_wren = emif_rd_readdatavalid;
wire [191:0] linebuf_wrdata = {emif_rd_rdata[23:0], emif_rd_rdata[55:32], emif_rd_rdata[87:64], emif_rd_rdata[119:96],
emif_rd_rdata[151:128], emif_rd_rdata[183:160], emif_rd_rdata[215:192], emif_rd_rdata[247:224]};
wire [9:0] linebuf_wraddr = {blocks_copied + (line_id ? 0 : 320)};
wire [19:0] emif_rd_block_addr = {ypos_lb_next, blocks_copied};
reg line_id_brclk_sync1_reg, line_id_brclk_sync2_reg, line_id_brclk_sync3_reg;
linebuf_double linebuf_rgb (
.data(linebuf_wrdata),
.rdaddress(linebuf_rdaddr),
.rdclock(PCLK_OUT_i),
.rdclocken(lb_enable),
.wraddress(linebuf_wraddr),
.wrclock(emif_br_clk),
.wren(linebuf_wren),
.wrclocken(lb_enable),
.q({R_linebuf, G_linebuf, B_linebuf})
);
// BRAM linebuffer operation
always @(posedge emif_br_clk or posedge emif_br_reset) begin
if (emif_br_reset) begin
emif_rd_read <= 1'b0;
emif_rd_burstcount <= 1'b0;
end else begin
if (emif_rd_burstcount > 0) begin // always finish read first, make sure doesn't last longer than line length
if (emif_rd_readdatavalid) begin
blocks_copied <= blocks_copied + 1'b1;
emif_rd_burstcount <= emif_rd_burstcount - 1'b1;
end
if (!emif_rd_waitrequest)
emif_rd_read <= 1'b0;
`ifdef DEBUG
emif_rd_line_missed <= (line_id_brclk_sync2_reg != line_id_brclk_sync3_reg);
`endif
end else if (line_id_brclk_sync2_reg != line_id_brclk_sync3_reg) begin
blocks_copied <= 0;
end else if (lb_enable & (blocks_copied < blocks_per_line)) begin
emif_rd_read <= 1'b1;
emif_rd_burstcount <= ((blocks_per_line-blocks_copied) > EMIF_RD_MAXBURST) ? EMIF_RD_MAXBURST : (blocks_per_line-blocks_copied);
emif_rd_addr <= {3'b001, emif_rd_block_addr, 5'h0};
end
line_id_brclk_sync1_reg <= line_id;
line_id_brclk_sync2_reg <= line_id_brclk_sync1_reg;
line_id_brclk_sync3_reg <= line_id_brclk_sync2_reg;
end
end
end else begin // EMIF_ENABLE
/* ------------------------------ BRAM lb interface ------------------------------ */
reg [5:0] ypos_wraddr;
reg [10:0] ypos_prev;
reg [10:0] xpos_wraddr;
reg [23:0] linebuf_wrdata;
reg linebuf_wren;
wire [16:0] linebuf_wraddr = {ypos_wraddr[($clog2(NUM_LINE_BUFFERS)-1):0], xpos_wraddr};
wire [16:0] linebuf_rdaddr = {ypos_lb[($clog2(NUM_LINE_BUFFERS)-1):0], xpos_lb[10:0]};
linebuf linebuf_rgb (
.data(linebuf_wrdata),
.rdaddress(linebuf_rdaddr),
.rdclock(PCLK_OUT_i),
.rdclocken(lb_enable),
.wraddress(linebuf_wraddr),
.wrclock(PCLK_CAP_i),
.wren(linebuf_wren),
.wrclocken(lb_enable),
.q({R_linebuf, G_linebuf, B_linebuf})
);
// Linebuffer write address calculation
always @(posedge PCLK_CAP_i) begin
if (ypos_i == 0) begin
ypos_wraddr <= 0;
end else if (ypos_i != ypos_prev) begin
if (ypos_wraddr == NUM_LINE_BUFFERS-1)
ypos_wraddr <= 0;
else
ypos_wraddr <= ypos_wraddr + 1'b1;
end
xpos_wraddr <= xpos_i;
ypos_prev <= ypos_i;
linebuf_wrdata <= {R_i, G_i, B_i};
linebuf_wren <= DE_i & datavalid_i;
end
end
endgenerate
endmodule