OpenCores
URL https://opencores.org/ocsvn/mpeg2fpga/mpeg2fpga/trunk

Subversion Repositories mpeg2fpga

[/] [mpeg2fpga/] [trunk/] [rtl/] [mpeg2/] [osd.v] - Rev 2

Compare with Previous | Blame | View Log

/* 
 * osd.v
 * 
 * Copyright (c) 2007 Koen De Vleeschauwer. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE.
 */
 
/*
 * osd.v - On-Screen Display
 */
 
`include "timescale.v"
 
`undef DEBUG
//`define DEBUG 1
 
/*
 * The On-Screen Display (OSD) is used for menus and user interaction.
 * It offers a palette of 256 24-bit colors, transparency and blinking.
 * The osd has the same resolution as the mpeg video being shown.
 *
 * Each osd pixel is looked up in the osd color lookup table.
 * The osd color lookup table returns, for every 8-bit osd pixel, 32 bits:
 *   y (8 bit), u (8 bit), v(8 bit), m(8 bit).
 * y, u, and v are the luma and chroma values of the osd color.
 * m is the osd color mode, and determines the displayed pixel according
 * to the following table:
 *   m (mode)
 *   xxx00000: alpha = 0/16
 *   xxx00001: alpha = 1/16
 *   xxx00010: alpha = 2/16
 *   xxx00011: alpha = 3/16
 *   xxx00100: alpha = 4/16
 *   xxx00101: alpha = 5/16
 *   xxx00110: alpha = 6/16
 *   xxx00111: alpha = 7/16
 *   xxx01000: alpha = 8/16
 *   xxx01001: alpha = 9/16
 *   xxx01010: alpha = 10/16
 *   xxx01011: alpha = 11/16
 *   xxx01100: alpha = 12/16
 *   xxx01101: alpha = 13/16
 *   xxx01110: alpha = 14/16
 *   xxx01111: alpha = 15/16
 *   xxx11111: alpha = 16/16
 *   xx0xxxxx: attenuate mpeg pixel
 *   xx1xxxxx: alpha blend osd and mpeg pixel
 *   00xxxxxx: output is mpeg pixel
 *   01xxxxxx: output is attenuated/alpha blended pixel
 *   10xxxxxx: output is osd pixel
 *   11xxxxxx: blink; alternate between osd and attenuated/alpha blended pixel
 *
 * By combining these values, it is possible to show an OSD, either blinking
 * or static, on a black or a transparent background.
 */
 
module osd (
  clk, clk_en, rst,
  y_in, u_in, v_in, h_sync_in, v_sync_in, pixel_en_in, osd_in,
  y_out, u_out, v_out, h_sync_out, v_sync_out, pixel_en_out,
  osd_clt_rd_addr, osd_clt_rd_en, osd_clt_rd_dta, 
  osd_enable, interlaced
  );
  input  clk;
  input  clk_en;
  input  rst;
 
  input [7:0]y_in;
  input [7:0]u_in;
  input [7:0]v_in;
 
  output reg [7:0]y_out;
  output reg [7:0]u_out;
  output reg [7:0]v_out;
 
  input pixel_en_in;
  input h_sync_in;
  input v_sync_in;
 
  output reg pixel_en_out;
  output reg h_sync_out;
  output reg v_sync_out;
 
 /* OSD pixel value */
  input      [7:0]osd_in;
 
 /* OSD color look-up table */
  output reg [7:0]osd_clt_rd_addr;
  output reg      osd_clt_rd_en;
  input     [31:0]osd_clt_rd_dta;
  input           osd_enable;
  input           interlaced;
 
  reg blink;
 
 /* 
  * stage 0
  * Look up osd color
  */
  reg [7:0]y_0;
  reg [7:0]u_0;
  reg [7:0]v_0;
  reg      pixel_en_0;
  reg      h_sync_0;
  reg      v_sync_0;
 
  always @(posedge clk)
    if (~rst) osd_clt_rd_addr <= 8'b0;
    else if (clk_en && pixel_en_in) osd_clt_rd_addr <= osd_in;
    else osd_clt_rd_addr <= osd_clt_rd_addr;
 
  always @(posedge clk)
    if (~rst) osd_clt_rd_en <= 1'b0;
    else if (clk_en) osd_clt_rd_en <= pixel_en_in;
    else osd_clt_rd_en <= osd_clt_rd_en;
 
  always @(posedge clk)
    if (~rst) {y_0, u_0, v_0} <= 24'b0;
    else if (clk_en && pixel_en_in) {y_0, u_0, v_0} <= {y_in, u_in, v_in};
    else {y_0, u_0, v_0} <= {y_0, u_0, v_0};
 
  always @(posedge clk)
    if (~rst) {pixel_en_0, h_sync_0, v_sync_0} <= 3'b0;
    else if (clk_en) {pixel_en_0, h_sync_0, v_sync_0} <= {pixel_en_in, h_sync_in, v_sync_in};
    else {pixel_en_0, h_sync_0, v_sync_0} <= {pixel_en_0, h_sync_0, v_sync_0};
 
 /* 
  * stage 1
  * Wait for osd clt lookup
  */
  reg [7:0]y_1;
  reg [7:0]u_1;
  reg [7:0]v_1;
  reg      pixel_en_1;
  reg      h_sync_1;
  reg      v_sync_1;
 
  always @(posedge clk)
    if (~rst) {y_1, u_1, v_1} <= 24'b0;
    else if (clk_en) {y_1, u_1, v_1} <= {y_0, u_0, v_0};
    else {y_1, u_1, v_1} <= {y_1, u_1, v_1};
 
  always @(posedge clk)
    if (~rst) {pixel_en_1, h_sync_1, v_sync_1} <= 3'b0;
    else if (clk_en) {pixel_en_1, h_sync_1, v_sync_1} <= {pixel_en_0, h_sync_0, v_sync_0};
    else {pixel_en_1, h_sync_1, v_sync_1} <= {pixel_en_1, h_sync_1, v_sync_1};
 
 /* 
  * stage 2
  * Read osd color 
  */
  reg [7:0]y_2;
  reg [7:0]u_2;
  reg [7:0]v_2;
  reg [7:0]y_blend_2;
  reg [7:0]u_blend_2;
  reg [7:0]v_blend_2;
  reg [7:0]osd_y_2;      /* stage 2: osd lumi */
  reg [7:0]osd_u_2;      /* stage 2: osd chromi */
  reg [7:0]osd_v_2;      /* stage 2: osd chromi */
  reg [3:0]osd_mode_2;   /* stage 2: osd mode: motion video, osd, blinking osd */
  reg [3:0]osd_transp_2; /* stage 2: osd transparency factor */
  reg      pixel_en_2;
  reg      h_sync_2;
  reg      v_sync_2;
 
  wire [7:0]osd_clt_y;
  wire [7:0]osd_clt_u;
  wire [7:0]osd_clt_v;
  wire [3:0]osd_clt_mode;
  wire [3:0]osd_clt_transp;
 
  assign {osd_clt_y, osd_clt_u, osd_clt_v, osd_clt_mode, osd_clt_transp} = osd_clt_rd_dta;
 
  always @(posedge clk)
    if (~rst) {osd_y_2, osd_u_2, osd_v_2, osd_mode_2, osd_transp_2} <= 32'b0;
    else if (clk_en) {osd_y_2, osd_u_2, osd_v_2, osd_mode_2, osd_transp_2} <= osd_clt_rd_dta;
    else {osd_y_2, osd_u_2, osd_v_2, osd_mode_2, osd_transp_2} <= {osd_y_2, osd_u_2, osd_v_2, osd_mode_2, osd_transp_2};
 
  always @(posedge clk)
    if (~rst) {y_2, u_2, v_2} <= 24'b0;
    else if (clk_en) {y_2, u_2, v_2} <= {y_1, u_1, v_1};
    else {y_2, u_2, v_2} <= {y_2, u_2, v_2};
 
  always @(posedge clk)
    if (~rst) {y_blend_2, u_blend_2, v_blend_2} <= 24'b0;
    else if (clk_en) {y_blend_2, u_blend_2, v_blend_2} <= osd_clt_mode[1] ? {osd_clt_y, osd_clt_u, osd_clt_v} : {8'd16, 8'd128, 8'd128}; /* (y, u, v) = (16, 128, 128) corresponds to black */
    else {y_blend_2, u_blend_2, v_blend_2} <= {y_blend_2, u_blend_2, v_blend_2};
 
  always @(posedge clk)
    if (~rst) {pixel_en_2, h_sync_2, v_sync_2} <= 3'b0;
    else if (clk_en) {pixel_en_2, h_sync_2, v_sync_2} <= {pixel_en_1, h_sync_1, v_sync_1};
    else {pixel_en_2, h_sync_2, v_sync_2} <= {pixel_en_2, h_sync_2, v_sync_2};
 
 /* 
  * stage 3-5
  * Transparency. Attenuate mpeg pixel.
  */ 
  wire [7:0]y_5;
  wire [7:0]u_5;
  wire [7:0]v_5;
  wire [7:0]y_transp_5;   /* stage 5: attenuated mpeg lumi */
  wire [7:0]u_transp_5;   /* stage 5: attenuated mpeg chromi */
  wire [7:0]v_transp_5;   /* stage 5: attenuated mpeg chromi */
  wire [7:0]osd_y_5;      /* stage 5: osd lumi */
  wire [7:0]osd_u_5;      /* stage 5: osd chromi */
  wire [7:0]osd_v_5;      /* stage 5: osd chromi */
  wire [3:0]osd_mode_5;   /* stage 5: osd mode: motion video, osd, transparent osd, blinking osd */
  wire      pixel_en_5;
  wire      h_sync_5;
  wire      v_sync_5;
 
  alpha_blend 
    #(.dta_width(55))
    alpha_blend_y (
    .clk(clk), 
    .clk_en(clk_en), 
    .rst(rst),
    .x_in(y_blend_2), 
    .y_in(y_2), 
    .dta_in({osd_y_2, osd_u_2, osd_v_2, osd_mode_2, y_2, u_2, v_2, pixel_en_2, h_sync_2, v_sync_2}),
    .z_out(y_transp_5), 
    .dta_out({osd_y_5, osd_u_5, osd_v_5, osd_mode_5, y_5, u_5, v_5, pixel_en_5, h_sync_5, v_sync_5}),
    .alpha_1(osd_mode_2[0]), 
    .alpha_2(osd_transp_2)
    );
 
  alpha_blend 
    #(.dta_width(1))
    alpha_blend_u (
    .clk(clk), 
    .clk_en(clk_en), 
    .rst(rst),
    .x_in(u_blend_2), 
    .y_in(u_2), 
    .dta_in(1'b0),
    .z_out(u_transp_5), 
    .alpha_1(osd_mode_2[0]), 
    .alpha_2(osd_transp_2),
    .dta_out()
    );
 
  alpha_blend 
    #(.dta_width(1))
    alpha_blend_v (
    .clk(clk), 
    .clk_en(clk_en), 
    .rst(rst),
    .x_in(v_blend_2), 
    .y_in(v_2), 
    .dta_in(1'b0),
    .z_out(v_transp_5), 
    .alpha_1(osd_mode_2[0]), 
    .alpha_2(osd_transp_2),
    .dta_out()
    );
 
 /*
  * stage 5
  * Select between mpeg pixel, attenuated mpeg pixel, osd pixel, or blinking osd.
  */
 
  always @(posedge clk)
    if (~rst) {y_out, u_out, v_out} <= 24'b0;
    else if (clk_en && ~pixel_en_5) {y_out, u_out, v_out} <= {8'd16, 8'd128, 8'd128};                                /* black during blanking */
    else if (clk_en && osd_enable)
      case (osd_mode_5[3:2])
        3'b00:  {y_out, u_out, v_out} <=         {y_5,        u_5,        v_5       }                              ; /* mpeg pixel */
        3'b01:  {y_out, u_out, v_out} <=         {y_transp_5, u_transp_5, v_transp_5}                              ; /* attenuated/alpha blended mpeg pixel */
        3'b10:  {y_out, u_out, v_out} <=                                                {osd_y_5, osd_u_5, osd_v_5}; /* osd pixel */
        3'b11:  {y_out, u_out, v_out} <= blink ? {y_transp_5, u_transp_5, v_transp_5} : {osd_y_5, osd_u_5, osd_v_5}; /* alternate between osd and attenuated/alpha blended mpeg pixel */
        default {y_out, u_out, v_out} <=         {y_5       , u_5       , v_5       }                              ; /* mpeg pixel */
      endcase
    else if (clk_en) {y_out, u_out, v_out} <= {y_5, u_5, v_5};                                                       /* osd switched off */
    else {y_out, u_out, v_out} <= {y_out, u_out, v_out};
 
  always @(posedge clk)
    if (~rst) {pixel_en_out, h_sync_out, v_sync_out} <= 3'b0;
    else if (clk_en) {pixel_en_out, h_sync_out, v_sync_out} <= {pixel_en_5, h_sync_5, v_sync_5};
    else {pixel_en_out, h_sync_out, v_sync_out} <= {pixel_en_out, h_sync_out, v_sync_out};
 
  /* Blinking */
 
  /*
   * count number of frames/fields. 
   * field_cnd increases by 1 on the falling edge of vsync 
   */
 
  reg [6:0]field_cnt;
 
  always @(posedge clk)
    if (~rst) field_cnt <= 0;
    else if (clk_en && (v_sync_1 == 1'b0) && (v_sync_2 == 1'b1)) field_cnt <= field_cnt + 1;
    else field_cnt <= field_cnt;
 
  always @(posedge clk)
    if (~rst) blink <= 0;
    else if (clk_en) blink <= interlaced ? field_cnt[6] : field_cnt[5]; // toggles every second or so. (Actually, every 32 frames if progressive, every 64 fields if interlaced)
    else blink <= blink;
 
`ifdef DEBUG
  always @(posedge clk)
    $strobe("%m\tin: %0d %0d %0d 1: %0d %0d %0d 2: %0d %0d %0d 5: %0d %0d %0d out: %0d %0d %0d enable: %d", y_in, u_in, v_in, y_1, u_1, v_1, y_2, u_2, v_2, y_5, u_5, v_5, y_out, u_out, v_out, osd_enable);
`endif
endmodule
 
/*
 * On-Screen Display Color Lookup Table 
 *
 * Register file writes to clt, 
 * osd reads from clt.
 */
`undef DEBUG
//`define DEBUG 1
 
module osd_clt (
  clk,
  rst,
  osd_clt_wr_en,
  osd_clt_wr_addr,
  osd_clt_wr_dta,
  dot_clk,
  dot_rst,
  osd_clt_rd_addr,
  osd_clt_rd_en,
  osd_clt_rd_dta);
 
  input        clk;
  input        rst;
  input        osd_clt_wr_en;
  input   [7:0]osd_clt_wr_addr;
  input  [31:0]osd_clt_wr_dta;
  input        dot_clk;
  input        dot_rst;
  input   [7:0]osd_clt_rd_addr;
  input        osd_clt_rd_en;
  output [31:0]osd_clt_rd_dta;
 
  reg     [7:0]clt_wr_addr;
  reg          clt_wr_en;
  reg    [31:0]clt_wr_dta;
 
  parameter [2:0]
    STATE_INIT  = 3'b001,
    STATE_CLEAR = 3'b010,
    STATE_RUN   = 3'b100;
 
  reg [2:0]next;
  reg [2:0]state;
 
  /*
   * state machine to initialize color-lookup table at reset
   */
 
  always @*
    case (state)
      STATE_INIT:  next = STATE_CLEAR;
      STATE_CLEAR: if (clt_wr_addr == 8'hff) next = STATE_RUN;
                   else next = STATE_CLEAR;
      STATE_RUN:   next = STATE_RUN;
      default:     next = STATE_INIT;
    endcase 
 
  always @(posedge clk)
    if (~rst) state <= STATE_INIT;
    else state <= next;
 
  always @(posedge clk)
    if (~rst) clt_wr_en <= 1'b0;
    else
      case (state)
        STATE_INIT:  clt_wr_en <= 1'b0;
	STATE_CLEAR: clt_wr_en <= 1'b1;
	STATE_RUN:   clt_wr_en <= osd_clt_wr_en;
	default      clt_wr_en <= 1'b0;
      endcase
 
  always @(posedge clk)
    if (~rst) clt_wr_addr <= 8'b0;
    else
      case (state)
        STATE_INIT:  clt_wr_addr <= 8'b0;
	STATE_CLEAR: clt_wr_addr <= clt_wr_addr + 8'b1;
	STATE_RUN:   clt_wr_addr <= osd_clt_wr_addr;
	default      clt_wr_addr <= 8'b0;
      endcase
 
  always @(posedge clk)
    if (~rst) clt_wr_dta <= 32'b0;
    else
      case (state)
        STATE_INIT:  clt_wr_dta <= 32'b0;
	STATE_CLEAR: clt_wr_dta <= 32'b0;
	STATE_RUN:   clt_wr_dta <= osd_clt_wr_dta;
	default      clt_wr_dta <= 32'b0;
      endcase
 
  /* OSD color look-up table */
 
  dpram_dc 
    #(.addr_width(8),                                         // number of bits in address bus
    .dta_width(32))                                           // number of bits in data bus
    osd_clt (
    .wr_rst(rst),                                             // reset, sync with write clock, active low
    .wr_clk(clk),                                             // write clock, rising edge trigger
    .wr_en(clt_wr_en),                                        // write enable, active high
    .wr_addr(clt_wr_addr),                                    // write address
    .din(clt_wr_dta),                                         // data input
    .rd_rst(dot_rst),                                         // reset, sync with read clock, active low
    .rd_clk(dot_clk),                                         // read clock, rising edge trigger
    .rd_en(osd_clt_rd_en),                                    // read enable, active high
    .rd_addr(osd_clt_rd_addr),                                // read address
    .dout(osd_clt_rd_dta)                                     // data output
    );
 
`ifdef DEBUG
  always @(posedge clk)
    $strobe("%m\tstate: %b clt_wr_en: %x clt_wr_addr: %x clt_wr_dta: %x", state, clt_wr_en, clt_wr_addr, clt_wr_dta);
`endif
endmodule
 
/*
 * Alpha blend x and y, using
 * z <= ( alpha_2 * x + ~alpha_2 * y + (alpha_1 ? x : y) + (1'b1 << (alpha_width - 1))) >> alpha_width;
 * where alpha_2 is 4 bits wide, alpha_width = 4.
 * To obtain alpha = 1, z = x set alpha_1 = 1, alpha_2 = 1111.
 * For values of alpha other than 1, set alpha_1 = 0.
 */
`undef DEBUG
//`define DEBUG 1
 
module alpha_blend (
  clk, clk_en, rst,
  x_in, y_in, alpha_1, alpha_2, dta_in, z_out, dta_out
  );
  parameter dta_width=8;
  input  clk;
  input  clk_en;
  input  rst;
 
  input [7:0]x_in;
  input [7:0]y_in;
  input      alpha_1;
  input [3:0]alpha_2;
  input [dta_width-1:0]dta_in;
 
  output reg [7:0]z_out;
  output reg [dta_width-1:0]dta_out;
 
  /* 
   * stage 1.
   */
 
  reg [12:0]x_prod_1;
  reg [12:0]y_prod_1;
  reg [dta_width-1:0]dta_1;
 
  wire [3:0]alpha_2_inv = ~alpha_2;
 
  always @(posedge clk)
    if (~rst) x_prod_1 <= 13'd0;
    else if (clk_en) x_prod_1 <= x_in * alpha_2 + 14'b1000;
    else x_prod_1 <= x_prod_1;
 
  always @(posedge clk)
    if (~rst) y_prod_1 <= 13'd0;
    else if (clk_en) y_prod_1 <= y_in * alpha_2_inv + (alpha_1 ? x_in : y_in);
    else y_prod_1 <= y_prod_1;
 
  always @(posedge clk)
    if (~rst) dta_1 <= 0;
    else if (clk_en) dta_1 <= dta_in;
    else dta_1 <= dta_1;
 
  /* 
   * stage 2.
   */
 
  reg [14:0]z_sum_2;
  wire [14:0]x_prod_1_ext = {1'b0, x_prod_1};
  wire [14:0]y_prod_1_ext = {1'b0, y_prod_1};
  reg [dta_width-1:0]dta_2;
 
  always @(posedge clk)
    if (~rst) z_sum_2 <= 15'd0;
    else if (clk_en) z_sum_2 <= (x_prod_1_ext + y_prod_1_ext) >> 4;
    else z_sum_2 <= z_sum_2;
 
  always @(posedge clk)
    if (~rst) dta_2 <= 0;
    else if (clk_en) dta_2 <= dta_1;
    else dta_2 <= dta_2;
 
  /* 
   * stage 3.
   */
 
  always @(posedge clk)
    if (~rst) z_out <= 8'd0;
    else if (clk_en) z_out <= (z_sum_2[14:8] == 7'b0) ? z_sum_2[7:0] : 8'd255;
    else z_out <= z_out;
 
  always @(posedge clk)
    if (~rst) dta_out <= 0;
    else if (clk_en) dta_out <= dta_2;
    else dta_out <= dta_out;
 
`ifdef DEBUG
  always @(posedge clk)
    $strobe("%m\tx_in: %d  y_in: %d  alpha_1: %d  alpha_2: %d  x_prod_1: %d  y_prod_1: %d  z_sum_2: %d  z_out: %d",
                 x_in, y_in, alpha_1, alpha_2, x_prod_1, y_prod_1, z_sum_2, z_out);
`endif
endmodule
/* not truncated */
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.