📄 yscale.v
字号:
// ================================================================================
// (c) 2004 Altera Corporation. All rights reserved.
// Altera products are protected under numerous U.S. and foreign patents, maskwork
// rights, copyrights and other intellectual property laws.
//
// This reference design file, and your use thereof, is subject to and governed
// by the terms and conditions of the applicable Altera Reference Design License
// Agreement (either as signed by you, agreed by you upon download or as a
// "click-through" agreement upon installation andor found at www.altera.com).
// By using this reference design file, you indicate your acceptance of such terms
// and conditions between you and Altera Corporation. In the event that you do
// not agree with such terms and conditions, you may not use the reference design
// file and please promptly destroy any copies you have made.
//
// This reference design file is being provided on an "as-is" basis and as an
// accommodation and therefore all warranties, representations or guarantees of
// any kind (whether express, implied or statutory) including, without limitation,
// warranties of merchantability, non-infringement, or fitness for a particular
// purpose, are specifically disclaimed. By making this reference design file
// available, Altera expressly does not recommend, suggest or require that this
// reference design file be used in combination with any other product not
// provided by Altera.
// ================================================================================
//---------------------------------------------------------------------------
// Y scaling
//---------------------------------------------------------------------------
`timescale 1ns/1ns
module yscale (
clk,
reset_n,
// input
rgb_empty,
rgb_rd,
rgb_in,
// output
rgb_out,
rgb_out_en,
eof,
// control registers
yscale_reg,
xlen,
ylen
);
input clk;
input reset_n;
input rgb_empty;
output rgb_rd;
input [17:0] rgb_in;
output [17:0] rgb_out;
output rgb_out_en;
output eof;
input [15:0] yscale_reg;
input [9:0] xlen;
input [8:0] ylen;
reg [9:0] line_addr;
reg [17:0] line1_rdata;
wire [17:0] line1_0_rdata;
wire [17:0] line1_1_rdata;
wire [17:0] line1_2_rdata;
reg [17:0] line2_rdata;
wire [17:0] line2_0_rdata;
wire [17:0] line2_1_rdata;
wire [17:0] line2_2_rdata;
reg [20:0] output_line;
wire [20:0] next_line;
wire [20:0] next_line2;
wire [3:0] line_diff;
wire [3:0] line_diff2;
wire line_wren;
wire line_end;
wire [3:0] scale_int = yscale_reg[15:12];
wire [5:0] scale_frac = yscale_reg[11:6];
//---------------------------------------------------------------------------
// Clock enable
//
// To allow for a max 3x scaling in the X scaling module, this module must
// only output pixels on every third clock.
//---------------------------------------------------------------------------
reg [1:0] clk_div;
always @(posedge clk or negedge reset_n)
if (~reset_n)
clk_div <= 2'b0;
else if (clk_div == 2'b10)
clk_div <= 2'b00;
else
clk_div <= clk_div + 2'b01;
wire clk_en = (clk_div == 2'b10);
//---------------------------------------------------------------------------
// Input state machine
//
// Idle during vertical blanking, we need to read two complete lines before
// we begin generating output lines.
//---------------------------------------------------------------------------
reg [3:0] in_state;
parameter in_idle = 4'b0000,
in_line2 = 4'b0001,
in_do = 4'b0010,
in_new = 4'b0011,
in_skip1 = 4'b0100,
in_skip2 = 4'b0101,
in_flush = 4'b0110,
in_wait1 = 4'b0111,
in_wait2 = 4'b1000;
reg in_done;
reg eof;
reg [8:0] line_in;
always @(posedge clk or negedge reset_n)
if (~reset_n)
line_in <= 9'b0;
else if ((in_state == in_flush) & line_end
& (next_line > {line_in, 12'b0})
| in_done & (in_state == in_skip1) & line_end & line_wren
& (output_line > {line_in, 12'b0})
| in_done & (in_state == in_new) & line_end & line_wren
& (next_line > {line_in, 12'b0}))
line_in <= 9'b0;
else if (line_end & line_wren)
line_in <= line_in + 9'b1;
always @(posedge clk or negedge reset_n)
if (~reset_n)
eof <= 1'b0;
else eof <= (clk_en & (in_state == in_flush) & line_end
& (next_line > {line_in, 12'b0})
| in_done & (in_state == in_skip1) & line_end & line_wren
& (output_line > {line_in, 12'b0})
| in_done & (in_state == in_new) & line_end & line_wren
& (next_line > {line_in, 12'b0}));
// No more input lines coming
always @(posedge clk or negedge reset_n)
if (~reset_n)
in_done <= 1'b0;
else if (line_end & line_wren & (line_in == ylen) & (yscale_reg[15:12] != 4'b0))
in_done <= 1'b1;
else if (output_line > {line_in, 12'b0})
in_done <= 1'b0;
always @(posedge clk or negedge reset_n)
if (~reset_n)
in_state <= in_idle;
else if (line_end & line_wren & (line_in == ylen))
in_state <= in_wait2;
else if (clk_en)
case (in_state)
in_idle:
// Idle or reading in first line of frame
if (line_end & line_wren)
in_state <= in_line2;
else
in_state <= in_idle;
in_line2:
// Reading in second line of frame
if (line_end & line_wren & (yscale_reg[15:12] == 4'h0))
// Scale up
in_state <= in_do;
else if (line_end & line_wren)
// Scale down or x1
in_state <= in_new;
else
in_state <= in_line2;
in_do:
// Processing stored line
if (line_end & (line_diff2 != 4'h0))
// Get next line
in_state <= in_new;
else if (line_end)
// Gap to allow X scale time to process end of line
in_state <= in_wait1;
else
in_state <= in_do;
in_new:
// Reading in new line and processing previous lines
if (line_end & line_wren & (yscale_reg[15:12] == 4'h0) & (line_diff2 < 4'h2))
// Scale up
in_state <= in_wait1;
else if (line_end & line_wren & (yscale_reg[15:12] != 4'h0) & (line_diff == 4'h2))
// Scale down or x1
in_state <= in_skip1;
else if (line_end & line_wren & (yscale_reg[15:12] != 4'h0) & (line_diff > 4'h2))
// Scale down or x1
in_state <= in_skip2;
else if (in_done & (next_line > {line_in, 12'b0}))
// Scale down or x1 no more input lines
in_state <= in_idle;
else
in_state <= in_new;
in_skip2:
// Reading in new line with no processing
if (line_end & line_wren | in_done)
in_state <= in_skip1;
else
in_state <= in_skip2;
in_skip1:
// Reading in new line with no processing
if (line_end & line_wren)
in_state <= in_new;
else if (in_done & (output_line > {line_in, 12'b0}))
in_state <= in_idle;
else
in_state <= in_skip1;
in_flush:
// Flushing stored pixels
if (line_end & (next_line > {line_in, 12'b0}))
in_state <= in_idle;
else if (line_end)
in_state <= in_wait2;
else
in_state <= in_flush;
in_wait1:
// Delay to allow X scale time to process end of line
in_state <= in_do;
in_wait2:
// Delay to allow X scale time to process end of line
in_state <= in_flush;
default:
in_state <= in_idle;
endcase
//---------------------------------------------------------------------------
// Output line position
//
// Scale factor is added to a running total to tell us where an output line
// is to be generated.
//
// E.g. yscale reg is 15'h1800 (meaning 'd1.5) or a scale factor of 2/3
// Output lines are 1, 2.5, 4, ... The fractional part tell us the proprtions
// of the two lines to be added (1-fraction)*line 1 + fraction*line2.
//
// The difference in the integer parts of the current and next two output
// lines tell us when to load the next input line.
//---------------------------------------------------------------------------
assign next_line = output_line + yscale_reg;
assign next_line2 = next_line + yscale_reg;
assign line_diff = next_line[15:12] - output_line[15:12];
assign line_diff2 = next_line2[15:12] - output_line[15:12];
always @(posedge clk or negedge reset_n)
if (~reset_n)
output_line <= 21'h1000;
else if (eof)
output_line <= 21'h1000;
else if (clk_en & ((line_addr == xlen) & line_wren & (in_state == in_new)
| line_end & (in_state == in_do)
| line_end & (in_state == in_flush)))
output_line <= next_line;
//---------------------------------------------------------------------------
// Line buffer controls
//---------------------------------------------------------------------------
assign line_wren = clk_en & ~rgb_empty & ((in_state == in_idle)
| (in_state == in_line2)
| (in_state == in_new)
| (in_state == in_skip1)
| (in_state == in_skip2));
wire rgb_rd = line_wren;
always @(posedge clk or negedge reset_n)
if (~reset_n)
line_addr <= 10'b0;
else if (line_wren | clk_en & ((in_state == in_do)
| (in_state == in_flush)))
begin
if (line_end)
line_addr <= 10'b0;
else
line_addr <= line_addr + 10'b1;
end
assign line_end = (line_addr == xlen);
wire line_en = clk_en & (~rgb_empty & (in_state == in_new)
| (in_state == in_do)
| (in_state == in_flush));
//---------------------------------------------------------------------------
// Instantiate line buffer RAMs
//
// Quartus will only generate power of 2 depth memories but we require 768
// words so three M4Ks are instantiated for each line buffer.
//---------------------------------------------------------------------------
wire line_wren0 = line_wren & (line_addr[9:8] == 2'b00);
wire line_wren1 = line_wren & (line_addr[9:8] == 2'b01);
wire line_wren2 = line_wren & (line_addr[9:8] == 2'b10);
line_buffer_ram u_line2_0 (
.clock (clk),
.wraddress (line_addr[7:0]),
.wren (line_wren0),
.data (rgb_in),
.rdaddress (line_addr[7:0]),
.q (line2_0_rdata)
);
line_buffer_ram u_line2_1 (
.clock (clk),
.wraddress (line_addr[7:0]),
.wren (line_wren1),
.data (rgb_in),
.rdaddress (line_addr[7:0]),
.q (line2_1_rdata)
);
line_buffer_ram u_line2_2 (
.clock (clk),
.wraddress (line_addr[7:0]),
.wren (line_wren2),
.data (rgb_in),
.rdaddress (line_addr[7:0]),
.q (line2_2_rdata)
);
always @(line_addr or line2_0_rdata or line2_1_rdata or line2_2_rdata)
case (line_addr[9:8])
2'b00: line2_rdata = line2_0_rdata;
2'b01: line2_rdata = line2_1_rdata;
default: line2_rdata = line2_2_rdata;
endcase
line_buffer_ram u_line1_0 (
.clock (clk),
.wraddress (line_addr[7:0]),
.wren (line_wren0),
.data (line2_rdata),
.rdaddress (line_addr[7:0]),
.q (line1_0_rdata)
);
line_buffer_ram u_line1_1 (
.clock (clk),
.wraddress (line_addr[7:0]),
.wren (line_wren1),
.data (line2_rdata),
.rdaddress (line_addr[7:0]),
.q (line1_1_rdata)
);
line_buffer_ram u_line1_2 (
.clock (clk),
.wraddress (line_addr[7:0]),
.wren (line_wren2),
.data (line2_rdata),
.rdaddress (line_addr[7:0]),
.q (line1_2_rdata)
);
always @(line_addr or line1_0_rdata or line1_1_rdata or line1_2_rdata)
case (line_addr[9:8])
2'b00: line1_rdata = line1_0_rdata;
2'b01: line1_rdata = line1_1_rdata;
default: line1_rdata = line1_2_rdata;
endcase
//---------------------------------------------------------------------------
// Instantiate Multipliers
//
// One each for red, green and blue for each line buffer. May eventually be
// reduced by resource sharing.
//---------------------------------------------------------------------------
wire [12:0] line1_red;
wire [12:0] line1_green;
wire [12:0] line1_blue;
wire [11:0] line2_red;
wire [11:0] line2_green;
wire [11:0] line2_blue;
reg [5:0] line2_fraction;
// delay to avoid problem with output_line changing
always @(posedge clk or negedge reset_n)
if (~reset_n)
line2_fraction <= 6'b0;
else
line2_fraction <= output_line[11:6];
wire [6:0] line1_fraction = 7'b1000000 - {1'b0, line2_fraction};
// line 1 red
mult6x7 u_mult_1r (
.clock (clk),
.dataa (line1_rdata[17:12]),
.datab (line1_fraction),
.result (line1_red)
);
// line 1 green
mult6x7 u_mult_1g (
.clock (clk),
.dataa (line1_rdata[11:6]),
.datab (line1_fraction),
.result (line1_green)
);
// line 1 blue
mult6x7 u_mult_1b (
.clock (clk),
.dataa (line1_rdata[5:0]),
.datab (line1_fraction),
.result (line1_blue)
);
// line 2 red
mult6x6 u_mult_2r (
.clock (clk),
.dataa (line2_rdata[17:12]),
.datab (line2_fraction),
.result (line2_red)
);
// line 2 green
mult6x6 u_mult_2g (
.clock (clk),
.dataa (line2_rdata[11:6]),
.datab (line2_fraction),
.result (line2_green)
);
// line 2 blue
mult6x6 u_mult_2b (
.clock (clk),
.dataa (line2_rdata[5:0]),
.datab (line2_fraction),
.result (line2_blue)
);
wire [5:0] red_out = (line1_red + line2_red) >> 6;
wire [5:0] green_out = (line1_green + line2_green) >> 6;
wire [5:0] blue_out = (line1_blue + line2_blue) >> 6;
wire [17:0] rgb_out = {red_out, green_out, blue_out};
wire rgb_out_en = line_en;
endmodule // yscale
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -