// A simple OSD implementation. Can be hooked up between a cores // VGA output and the physical VGA pins module osd ( input clk_sys, input io_osd, input io_strobe, input [15:0] io_din, input clk_video, input [23:0] din, output [23:0] dout, input de_in, output reg de_out, output reg osd_status ); parameter OSD_COLOR = 3'd4; parameter OSD_X_OFFSET = 12'd0; parameter OSD_Y_OFFSET = 12'd0; localparam OSD_WIDTH = 12'd256; localparam OSD_HEIGHT = 12'd64; `ifdef OSD_HEADER localparam OSD_HDR = 12'd32; `else localparam OSD_HDR = 12'd0; `endif reg osd_enable; reg [7:0] osd_buffer[OSD_HDR ? (4096+1024) : 4096]; reg info = 0; reg [8:0] infoh; reg [8:0] infow; reg [11:0] infox; reg [21:0] infoy; reg [21:0] hrheight; always@(posedge clk_sys) begin reg [12:0] bcnt; reg [7:0] cmd; reg has_cmd; reg old_strobe; reg highres = 0; hrheight <= info ? infoh : ((OSD_HEIGHT<> 9) > 1) ? (((cnt+1'b1) >> 9) - 1) : 0; pixcnt <= 0; end end reg [2:0] osd_de; reg osd_pixel; reg [21:0] v_cnt; reg v_cnt_below320, v_cnt_below640, v_cnt_below960; reg [21:0] v_osd_start_320, v_osd_start_640, v_osd_start_960, v_osd_start_other; // pipeline the comparisons a bit always @(posedge clk_video) if(ce_pix) begin v_cnt_below320 <= v_cnt < 320; v_cnt_below640 <= v_cnt < 640; v_cnt_below960 <= v_cnt < 960; v_osd_start_320 <= ((v_cnt-hrheight)>>1) + OSD_Y_OFFSET; v_osd_start_640 <= ((v_cnt-(hrheight<<1))>>1) + OSD_Y_OFFSET; v_osd_start_960 <= ((v_cnt-(hrheight + (hrheight<<1)))>>1) + OSD_Y_OFFSET; v_osd_start_other <= ((v_cnt-(hrheight<<2))>>1) + OSD_Y_OFFSET; end always @(posedge clk_video) begin reg deD; reg [1:0] osd_div; reg [1:0] multiscan; reg [7:0] osd_byte; reg [23:0] h_cnt; reg [21:0] dsp_width; reg [21:0] osd_vcnt; reg [21:0] h_osd_start; reg [21:0] v_osd_start; reg [21:0] osd_hcnt; reg osd_de1,osd_de2; reg [1:0] osd_en; if(ce_pix) begin deD <= de_in; if(~&h_cnt) h_cnt <= h_cnt + 1'd1; if(~&osd_hcnt) osd_hcnt <= osd_hcnt + 1'd1; if (h_cnt == h_osd_start) begin osd_de[0] <= osd_en[1] && hrheight && (info ? (osd_vcnt < hrheight) : (!osd_vcnt[11:7] || (osd_vcnt[11] && osd_vcnt[7] && (osd_vcnt[6:0] >= 4) && (osd_vcnt[6:0] < 19)))); osd_hcnt <= 0; end if (osd_hcnt+1 == (info ? infow : OSD_WIDTH)) osd_de[0] <= 0; // falling edge of de if(!de_in && deD) dsp_width <= h_cnt[21:0]; // rising edge of de if(de_in && !deD) begin h_cnt <= 0; v_cnt <= v_cnt + 1'd1; h_osd_start <= info ? infox : (((dsp_width - OSD_WIDTH)>>1) + OSD_X_OFFSET - 2'd2); if(h_cnt > {dsp_width, 2'b00}) begin v_cnt <= 1; osd_en <= (osd_en << 1) | osd_enable; if(~osd_enable) osd_en <= 0; if(v_cnt_below320) begin multiscan <= 0; v_osd_start <= info ? infoy : v_osd_start_320; end else if(v_cnt_below640) begin multiscan <= 1; v_osd_start <= info ? (infoy<<1) : v_osd_start_640; end else if(v_cnt_below960) begin multiscan <= 2; v_osd_start <= info ? (infoy + (infoy << 1)) : v_osd_start_960; end else begin multiscan <= 3; v_osd_start <= info ? (infoy<<2) : v_osd_start_other; end end osd_div <= osd_div + 1'd1; if(osd_div == multiscan) begin osd_div <= 0; if(~osd_vcnt[10]) osd_vcnt <= osd_vcnt + 1'd1; if(osd_vcnt == 'b100010011111 && ~info) osd_vcnt <= 0; end if(v_osd_start == v_cnt) {osd_div, osd_vcnt} <= OSD_HDR ? {~info, 3'b000, ~info, 7'b0000000} : 22'd0; end osd_byte <= osd_buffer[{osd_vcnt[7:3], osd_hcnt[7:0]}]; osd_pixel <= osd_byte[osd_vcnt[2:0]]; osd_de[2:1] <= osd_de[1:0]; end end reg [23:0] rdout; assign dout = rdout; reg [23:0] osd_rdout, normal_rdout; reg osd_mux; reg de_dly; always @(posedge clk_video) begin normal_rdout <= din; osd_rdout <= {{osd_pixel, osd_pixel, OSD_COLOR[2], din[23:19]},// 23:16 {osd_pixel, osd_pixel, OSD_COLOR[1], din[15:11]},// 15:8 {osd_pixel, osd_pixel, OSD_COLOR[0], din[7:3]}}; // 7:0 osd_mux <= ~osd_de[2]; rdout <= osd_mux ? normal_rdout : osd_rdout; de_dly <= de_in; de_out <= de_dly; end endmodule