NTSC to VGA Converter Marco Moreno Adrian De La Rosa EE382M-4 Adv Emb Arch 1 Project Goal NTSC Video Source RCA Video Cable TLL5000 Spartan3 FPGA 15-pin VGA Cable 2 Project Challenges • YUV -> RGB color space conversion • Input to display sync timing • Interlaced video to progressive scan Project Hardware • TLL5000 – base development module – Analog Devices: ADV7180 - NTSC decoder – Analog Devices: ADV7125 - VGA DAC – Xilinx: Spartan III - FPGA • TLL6219 – MX21 daughter board • Digital camera NTSC source + A/V cable • LCD monitor + 15-pin VGA cable 4 ADV7180 NTSC Decoder • Analog Input – Composite – Component – Svideo • Digital Outputs – LLC 27MHz clock – HS, VS/Field – 8-bit Output Port P 5 ADV7180 Block Diagram 6 ADV7125 Video DAC Converts 8-bit values on RGB ports to analog levels for display 7 Color-Space Conversion • YUV Data Format • RGB Data Format • Color-space conversion • Conversion quality / speed 8 YUV Data Format • Compatible with black & white TV infrastructure • U & V color difference signals. • Single Image format • Video format 9 YUV Analyzer Sunray Image – YUV Tools http://www.sunrayimage.com/ Analysis data used for conversion quality reference 10 RGB Data Format • Additive color model • R, G and B are color contributions rather than color differences • Single Image Format • Video Format – Three channels for color, 8-bit values – VGA connector – separate lines for vertical and horizontal synchronization signals. 11 Color-space conversion R = 1.164*((int)Y - 16) + 1.596*((int)V - 128); G = 1.164*((int)Y - 16) - 0.714*((int)V - 128) - 0.344*((int)U-128); B = 1.164*((int)Y - 16) + 2.018*((int)U - 128); • Multiple sources for information • Multiple recommendations for values used in calculation • Ultimately chose a mix to give values closed to reference YUV tool analyzer 12 Conversion C++ code //memory allocation int *yuv = (volatile unsigned int *)malloc(YUVBLOCK); //Open the file for reading in binary format int fd = open("flower_droplet.uyvy", O_RDWR); //write file to yuv memory block read(fd, yuv, YUVBLOCK); close(fd); //open file for writing fd = open ("converted.bmp", O_CREAT|O_RDWR); printf("fd %d\n", fd); // write header write(fd,&header[0],4); . . . write(fd,&header[13],2); //Calculate values and store to array R = 1.164*((int)Y - 16) + 1.596*((int)V - 128); G = 1.164*((int)Y - 16) - 0.714*((int)V - 128) - 0.344*((int)U-128); B = 1.164*((int)Y - 16) + 2.018*((int)U - 128); • • • • • Allocate memory Open file and write to mem Create new file Write header for new file Calculate all pixel RGB data • Write out to new file • Rev 1.0 :~4hr • Rev 2.0 : ~4sec //Write entire array to memory at once write(fd,&BLOCK[0],YUVBLOCK); printf("\n\n"); close(fd); 13 Conversion Verilog code • Implemented with shifts and adds only • Asynchronous logic block • Bitwise operations // CALCULATE Y COMPONENT OF R, G AND B assign ycomp = ((y & 8'hf0) == 8'h00) ? (8'h00 - y_16 - {3'b0, y_16[7:3]}) : ((y_16) + {3'b0, y_16[7:3]} + {5'b0, y_16[7:5]} + {7'b0, y_16[7]}); // CALCULATE V COMPONENT OF R assign r_vcomp = ((v & 8'h80) == 8'h00) ? (9'd0 - v_128 - {3'b0, v_128[7:3]} - {4'b0, v_128[7:4]}) : (v_128 + {3'b0, v_128[7:3]} + {4'b0, v_128[7:4]}); // CALCULATE V COMPONENT OF G assign g_vcomp = ((v & 8'h80) == 8'h00) ? (9'b0 - {1'b0, v_128[7:1]} - {3'b0, v_128[7:3]} - {4'b0, v_128[7:4]} - {5'b0, v_128[7:5]}) : ({1'b0, v_128[7:1]} + {3'b0, v_128[7:3]} + {4'b0, v_128[7:4]} + {5'b0, v_128[7:5]}); // CALCULATE U COMPONENT OF G assign g_ucomp = ((u & 8'h80) == 8'h00) ? (9'b0 - {2'b0, u_128[7:2]} - {4'b0, u_128[7:4]} - {5'b0, u_128[7:5]} - {6'b0, u_128[7:6]}) : ({2'b0, u_128[7:2]} + {4'b0, u_128[7:4]} + {5'b0, u_128[7:5]} + {6'b0, u_128[7:6]}); // CALCULATE U COMPONENT OF B assign b_ucomp = ((u & 8'h80) == 8'h00) ? (9'b0 - {u_128, 1'b0} - {6'b0, u_128[7:6]}) : ({u_128, 1'b0} + {6'b0, u_128[7:6]}); // ADD COMPONENTS TO CALCULATE R, G AND B assign r_wire = ycomp + r_vcomp; assign g_wire = ycomp - g_vcomp - g_ucomp; assign b_wire = ycomp + b_ucomp; // CHECK FOR R, G OR B LESS THAN ZERO assign r_zero = (r_wire[8] && 1) ? (9'h00) : (r_wire); assign g_zero = (g_wire[8] && 1) ? (9'h00) : (g_wire); assign b_zero = (b_wire[8] && 1) ? (9'h00) : (b_wire); // CHECK FOR R, G, OR B GREATER THAN 255 AND SET FINAL VALUE assign r = (r_zero[8:7] > 255) ? (8'hff) : (r_zero[7:0]); assign g = (g_zero[8:7] > 255) ? (8'hff) : (g_zero[7:0]); assign b = (b_zero[8:7] > 255) ? (8'hff) : (b_zero[7:0]); Y-component = (y - 16) * 1.0010101; Y-component = (y - 16) + (Y - 16 ) >> 3 + (y - 16) >> 5 + (Y - 16) >> 7; V-component of R = (V - 128) * 1.0011; V-component of R = (V - 128) + (V - 128) >> 3 + (V - 128) >> 4; V-component of G = (V - 128) * .10111 V-component of G = (V - 128) * (V - 128) >> 1 + (V - 128) >> 3 + (V - 128) >> 4 + (V - 128) >> 5; U-component of G = (U - 128) * .010111; U-component of G = (U - 128) >> 2 + (U - 128) >> 4 + (U - 128) >> 5 + (U - 128) >> 6; U-component of B = (U - 128) * 10.000001; U-component of B = (U - 128) << 1 + (U - 128) >> 6; 14 Conversion quality / speed Pixels Y 0x4b V 0x7c 75 R Ref. C++ Verilog Y 0x4b 124 G 62 62 63 U 0x67 75 B 82 80 76 18 18 17 103 Unoptimized C++ code Rev 1.0 : ~ 4hr Rev 2.0 : ~4sec FPGA operating @ ~27Mhz pixel clock Frame rate 60 Hz One frame - ~16 msec 15 Video Synchronization • NTSC to VGA timing generation • Interlaced video to progressive scan • Solution architecture 16 VGA Sync Signals 17 VGA Sync Timing (Typical) Horizontal Regions Vertical Regions VGA mode Resolution (HxV) Pixel clock(Mhz) a(ms) b(us) c(us) d(us) a(lines) b(lines) c(lines) d(lines) VGA(60Hz) 640x480 25 (640/c) 3.8 1.9 25.4 0.6 2 33 480 10 VGA(85Hz) 640x480 36 (640/c) 1.6 2.2 17.8 1.6 3 25 480 1 SVGA(60Hz) 800x600 40 (800/c) 3.2 2.2 20 1 4 23 600 1 SVGA(75Hz) 800x600 49 (800/c) 1.6 3.2 16.2 0.3 3 21 600 1 SVGA(85Hz) 800x600 56 (800/c) 1.1 2.7 14.2 0.6 3 27 600 1 XGA(60Hz) 1024x768 65 (1024/c) 2.1 2.5 15.8 0.4 6 29 768 3 XGA(70Hz) 1024x768 75 (1024/c) 1.8 1.9 13.7 0.3 6 29 768 3 XGA(85Hz) 1024x768 95 (1024/c) 1.0 2.2 10.8 0.5 3 36 768 1 1280x1024(60Hz) 1280x1024 108 (1280/c) 1.0 2.3 11.9 0.4 3 38 1024 1 18 VGA Progressive Video Line Line Line Line 1 2 3 4 … Line Line Line Line 522 523 524 525 19 NTSC Interlaced Video 20 Field 2 start (odd lines) Generated VGA VSYNC VS/Field (from NTSC decoder) Generated VGA HSYNC HS (from NTSC decoder) Field 1 / Field 2 scanning Field 1 start (even lines) 21 De-Interlacing 22 Video Stream Architecture RCA Input ADV7180 NTSC Decoder LLC - 27MHz pixel clock VGA VSYNC Generator VGA VSYNC VGA HSYNC 4:2:2 De-Interlace / Pixel Reformat 4:4:4 Color Space Converter ADV7125 Video DAC VGA Analog Color Channels 23 De-Interlace Line Buffers WR_BUF_SEL WR_BUF_SEL ? WR_PTR : RD_PTR 2 x RAMB16_S18 WE ADDR[9:0] DIN[15:0] YUV 4:2:2 8-bits per clk RD_BUF_SEL ? WR_PTR : RD_PTR WR_BUF_SEL DOUT[15:0] 1 DOUT[15:0] 0 YUV 4:4:4 16-bits per clk DIN[15:0] ADDR[9:0] WE RD_BUF_SEL 24 ADV7180 I2C Interface – – – – Serial protocol for interconnecting IC’s I2C block registers memory mapped to CPU Linux mmap application command line interface Controls various timing and format options iMX.21 Registers 0xCC0000000xCC000100 Wishbone FPGA I2C Controller SCLK SDA ADV7180 NTSC Decoder Registers -Input Select -H/V SYNC shape -Video Format -Interrupt Control 25 Lessons Learned • Keep Verilog code simple • Edge triggers are extremely useful • Tie one-off possibilities to switches • Not all TLL5000 boards have same clocks 26 Potential future work • Complete optimization of C++ code • Implement video stream with ARM controller rather than full-fpga implementation • FPGA vs. ARM performance analysis • FPGA vs. ARM power analysis • Fix horizontal positioning error • Smooth de-interlace w/ interpolation • DMA image data to iMX.21 SDRAM 27 Backup 28 Hsync generation 29 Vsync generation 30 Field 1 / 2 & data transmission 31 Direct Y-data NTSC to VGA 32 RGB channel separation 33 Buffered, CSC, Partially Aligned 34