OV7670 and PPro


Wile_E_Coyote

Recommended Posts

NET "clk50" TNM_NET = clk50;TIMESPEC TS_clk50 = PERIOD "clk50" 31.25 ns HIGH 50%;NET "clk50"   LOC = "P94"; # Bank = 0, Pin name = IP_L13P_0/GCLK8, Type = GCLK, Sch name = GCLK0NET "OV7670_PWDN"  		LOC = "P134" | IOSTANDARD=LVTTL | SLEW=SLOW; # JA0NET "OV7670_RESET" 		LOC = "P133" | IOSTANDARD=LVTTL | SLEW=SLOW; # JA4NET "OV7670_DATA<0>"  	LOC = "P132" | IOSTANDARD=LVTTL | SLEW=SLOW; # JA1NET "OV7670_DATA<1>"  	LOC = "P131" | IOSTANDARD=LVTTL | SLEW=SLOW; # JA5NET "OV7670_DATA<2>"  	LOC = "P127" | IOSTANDARD=LVTTL | SLEW=SLOW; # JA2NET "OV7670_DATA<3>"  	LOC = "P126" | IOSTANDARD=LVTTL | SLEW=SLOW; # JA6NET "OV7670_DATA<4>"  	LOC = "P124" | IOSTANDARD=LVTTL | SLEW=SLOW; # JA3NET "OV7670_DATA<5>"  	LOC = "P123" | IOSTANDARD=LVTTL | SLEW=SLOW; # JA7NET "OV7670_DATA<6>"  	LOC = "P121" | IOSTANDARD=LVTTL | SLEW=SLOW; # JB0NET "OV7670_DATA<7>"  	LOC = "P120" | IOSTANDARD=LVTTL | SLEW=SLOW; # JB4NET "OV7670_XCLK"  		LOC = "P119" | IOSTANDARD=LVTTL | SLEW=SLOW; # JB1NET "OV7670_PCLK"  		LOC = "P118" | IOSTANDARD=LVTTL | SLEW=SLOW; # JB5NET "OV7670_HREF"  		LOC = "P117" | IOSTANDARD=LVTTL | SLEW=SLOW; # JB2NET "OV7670_VSYNC" 		LOC = "P116" | IOSTANDARD=LVTTL | SLEW=SLOW; # JB6NET "OV7670_SIOD"  		LOC = "P115" | IOSTANDARD=LVTTL | SLEW=SLOW | PULLUP; # JB3NET "OV7670_SIOC"  		LOC = "P114" | IOSTANDARD=LVTTL | SLEW=SLOW; # JB7NET "OV7670_PCLK" 		CLOCK_DEDICATED_ROUTE = FALSE;#NET "LED<0>"  LOC = "P79" 		| IOSTANDARD=LVCMOS25 ; #NET "LED<1>"  LOC = "P81" 		| IOSTANDARD=LVCMOS25 ; #NET "LED<2>"  LOC = "P83" 		| IOSTANDARD=LVCMOS25 ; #NET "LED<3>"  LOC = "P85" 		| IOSTANDARD=LVCMOS25 ; #NET "LED<4>"  LOC = "P88" 		| IOSTANDARD=LVCMOS25 ; #NET "LED<5>"  LOC = "P93" 		| IOSTANDARD=LVCMOS25 ; #NET "LED<6>"  LOC = "P98" 		| IOSTANDARD=LVCMOS25 ;  #NET "LED<7>"  LOC = "P100"  	| IOSTANDARD=LVCMOS25 ;  NET "vgaRed<0>"   LOC = "P78";NET "vgaRed<1>"   LOC = "P74";NET "vgaRed<2>"   LOC = "P95";NET "vgaGreen<0>" LOC = "P84";NET "vgaGreen<1>" LOC = "P82";NET "vgaGreen<2>" LOC = "P80";NET "vgaBlue<1>"  LOC = "P92";NET "vgaBlue<2>"  LOC = "P87";NET "vgaHSync"    LOC = "P97";NET "vgaVSync"    LOC = "P99";NET "button" LOC = "P47";

Hello.  I was inspired by Volkhur's (spelling?) OV7670 project, but I wanted to add the VGA output as an exercise, so I found Hamster's work with the Basys, and based my design entirely on his work.

 

I have made some progress, but I am hitting a roadblock.  At first, when I hooked up my TV's VGA port to the VGA port of the Mega Wing, the TV reported "SIGNAL NOT FOUND."  I found some errors in my constraints file and fixed those and now the TV reports, "UNABLE TO READ SIGNAL," or something to that effect.

 

I count this as progress, as the TV initially could not even see a signal, and now it sees the signal, but doesn't understand it.

 

Based on the warnings I got when I compiled the source files, I believe it's a PCLK issue.  I'm not sure if I can tell you exactly what sort of help I need, but I would appreciate it if you could look at the warning and see if you can help me identify what the possible issue might be.

 

I will go ahead and put my constraints in here, too.  I believe this is where the problem originates.

 

This might or might not be important information, but what I did was I connected the Mega Wing to my Pro with male-to-female wires.  I directly connected the VGA pins to the same location that it would be connected to on the Pro.  I also connected my OV7670 to the Pro with a set of male-to-female wires and located those to the headers where the LEDs and switches normally are on the Pro (Header pin set C0-C15).  The PCLK pin of the OV7670 module is physically connected to P118 (C4).  The warning I got seems to indicate that this is not ideal.

 

 

post-36880-0-84236600-1399600280_thumb.j

Link to comment
Share on other sites

I notice that it is still called "clk50" - are you sure that the clocking has all been adjusted to reflect the difference in clock speeds?

 

It it is anything like my project a valid VGA signal will be generated even if a camera isn't connected.

Link to comment
Share on other sites

Thanks for the reply, Hamster.  I didn't do any clock adjustments, except to change the period from 20 ns to 31.25 ns and the location to P94 for the Pro.

 

In light of your comment, I watched Jack's two DCM videos on YouTube.  Will I need to utilize the DCM?  I noticed on the datasheet that the XCLK signal should be between 10 and 48 MHz, with 24 MHz being typical.

 

I appreciate you sharing your project.  It has helped me TREMENDOUSLY with respect to instantiating modules and making connections.  I did not copy and paste anything from your project, I typed it out and renamed a lot of the signals and declarations.  I did this because I learn better when I use someone else's project as a boiler plate and customize it myself to see the relationships and test comprehension.

 

Thanks again.

Link to comment
Share on other sites

I should have said that I didn't copy and paste the source files. I obviously copied and pasted the constraints, but modified them to match my source files.

I attempted to use the ipcore generator for the DCM, but ISE said that none of them were compatible with my hardware. Is this normal? They were all grayed out.

Link to comment
Share on other sites

I should have said that I didn't copy and paste the source files. I obviously copied and pasted the constraints, but modified them to match my source files.

I attempted to use the ipcore generator for the DCM, but ISE said that none of them were compatible with my hardware. Is this normal? They were all grayed out.

 

It is an long-standing Installer bug - see http://www.xilinx.com/support/answers/53695.html

Link to comment
Share on other sites

Okay, so I'm running ISE Webpack 14.7 and this issue was supposed to be resolved with release 14.5 per the link you supplied.  I did manage to find the clock generator wizard and I was able to use it.  I created a module that reduced the on-board 32MHz clock to 24MHz.  When I rebuilt the top module, I still get the same warning.

 

A clock IOB / BUFGMUX clock component pair have been found
   that are not placed at an optimal clock IOB / BUFGMUX site pair. The clock
   IOB component <OV7670_PCLK> is placed at site <P118>. The corresponding BUFG
   component <OV7670_PCLK_BUFGP/BUFG> is placed at site <BUFGMUX_X2Y9>. There is
   only a select set of IOBs that can use the fast path to the Clocker buffer,
   and they are not being used.

 

I do have the PCLK pin on the breakout board connected to P118.  Is there a better location to place it that would address this issue?

 

I am asking because I'm still getting the "INVALID FORMAT" report from my TV.

Link to comment
Share on other sites

Thanks MK.  After reviewing the PPro schematic, I found a GCLK pin (P88).  This has cleared up the warning message.

 

I ran a simulation and something is wrong with the XCLK signal to the camera module.  This is the 24 MHz system clock input for the camera module.  A snipping of the simulation screen is attached.

 

I created a 24 MHz clock bus signal with the DCM tool and ultimately have it connected to the XCLK signal in my "ov7670Controller" module.  Here is the module, which is largely based on the work that hamster did.  In the entity, the input called "clk" is tied to the 24 MHz clock from the DCM.

library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;entity ov7670Controller is    Port ( clk			: in  	STD_LOGIC;           resend		: in  	STD_LOGIC;           SIOD			: inout  STD_LOGIC;           configFinished	: out  	STD_LOGIC;           SIOC			: out  	STD_LOGIC;           reset		: out  	STD_LOGIC;           PWDN			: out  	STD_LOGIC;           XCLK			: out  	STD_LOGIC);end ov7670Controller;architecture Behavioral of ov7670Controller is	COMPONENT ov7670Registers	PORT(		clk		: IN	std_logic;		advance		: IN	std_logic;		resend 		: IN	std_logic;          		command		: OUT 	std_logic_vector(15 downto 0);		finished 	: OUT 	std_logic		);	END COMPONENT;		COMPONENT pseudoI2C	PORT(		clk		: IN	std_logic;		send		: IN	std_logic;		identification 	: IN	std_logic_vector(7 downto 0);		registers	: IN	std_logic_vector(7 downto 0);		value		: IN	std_logic_vector(7 downto 0);    		siod		: INOUT	std_logic;      		taken		: OUT 	std_logic;		sioc		: OUT 	std_logic		);	END COMPONENT;		signal sysClock		: std_logic :=	'0';	signal command		: std_logic_vector (15 downto 0);	signal finished		: std_logic :=	'0';	signal taken		: std_logic :=	'0';	signal send		: std_logic;	constant cameraAddress	: std_logic_vector (7 downto 0)	:= x"42";begin	configFinished	<=  finished;		send	  <=  not finished;		Inst_pseudoI2C: pseudoI2C PORT MAP(		clk		=> clk,		send		=> send,		taken		=> taken,		identification 	=> cameraAddress,		registers	=> command (15 downto 8),		value		=> command (7 downto 0),		siod		=> SIOD,		sioc		=> SIOC	);		reset	<=	'1';	--Normal Mode	PWDN	<=	'0';	--Power device up	XCLK	<=	sysClock;		Inst_ov7670Registers: ov7670Registers PORT MAP(		clk		=> clk,		advance		=> taken,		resend 		=> resend,		command		=> command,		finished 	=> finished	);		process (clk)	begin		if rising_edge(clk) then			sysClock <= not sysClock;		end if;	end process;end Behavioral;

post-36880-0-14119500-1399813026_thumb.j

Link to comment
Share on other sites

Okay, some progress!  I now have a picture, but it's extremely grainy and the color is all wrong.  I remembered that the resolution of the image I want to generate is clock-dependent, so I modified the DCM to create another output for 35 MHz for the 800x600 resolution.  I originally tied the 24 MHz signal to the VGA module, which obviously didn't work.  After adding another signal to tie this new clock signal to and adding it to the proper modules, I now have a "picture."

 

I am guessing that the other problems are related to other incorrect clock rates.  After creating a new test bench and re-running the simulation, I still have the XCLK conflicts and they look identical to the previously posted simulation results.  I can't find anything in my source files that show where there are conflicts, but I agree with you, Hamster...it definitely looks like two or more drivers are tied to the XCLK.

 

Here is a picture of the output.  Where would you suggest I look to fix this next?

 

post-36880-0-26103300-1399856419_thumb.j

 

 

Link to comment
Share on other sites

Out on a limb here... I think that your test bench has a process that is driving XCLK that shouldn't be there.

 

I think this because the XCLK is still toggling at 50MHz even though the DCM has yet to lock  and start driving XCLK (during the first 100us).

Link to comment
Share on other sites

Hi,

 

have you registered the physical outputs with the pixel clock?

As a rule of thumb, you aren't allowed to use any combinational (non-registered) logic after the final register.

 

Personally, I'd' try to break down the problem to the smallest possible test case.

 

Expect broken hardware. It happens. Sometimes, gentle pushing, bending, touching causes a change to the result => bingo.

Link to comment
Share on other sites

All right...I'm stumped.  I have gone through the source files two dozen times or more and I still don't see what the problem is.  Everything in the source files makes sense to me and the connections make sense.  I went ahead and deleted the DCM I created and created a new one - this one converting my on-board 32-MHz clock to 50-MHz (just to make it match hamster's implementation).  I have this 50-MHz clock signal feeding all of the modules, so everything is running at the same clock frequency.  I'm still getting the same image on my TV.  And, yes, I did try pushing / bending / touching / fiddling with the wires and connections and no change.

 

The source files are too numerous and too large to post here, so I'm not sure how to get it out here for you more experienced guys to look at and find what I'm missing.  I suppose I could post each one in a separate post...I am connecting a LG 47" TV to the VGA port of the Mega Wing...do you think that I should maybe try to dig up a monitor and see if that makes a difference?  I don't have a monitor handy (I use a laptop).

 

Not sure I know how to answer your question, offroad (w.r.t. registering the physical outputs with the pixel clock).  I'm just getting started, so I don't really understand the question.

Link to comment
Share on other sites

Hi Wile_E_Coyote.

 

When data coming out of the FPGA there are two options. The register holding the value can be located in the I/O Block ("IOB" -really close to the pin) or it can be held in a general purpose register in the FPGA fabric. If outputs are held in the IOB, then all the signals will change pretty much at the same time, limited by clock skew across the IO bank being used, If outputs are registered within the FPGA fabric (and then passed thorugh to an IOB's I/O buffer) then the signal skew is determined by lots of things. THings like which register is in use, how the router decided to run the signal, the operating temperature of the chip and so on.  

 

The same happens wiith the input signals too. If your parallel signals are registered within the IOB then all data lines will be captured at the same time. If the register is in the FPGA then the signal will be delayed.

 

You can address this  with the ""Pack registers into IOB"" property for the mapping process, explict use of I/O primatives within your design, or using VHDL signal attributes (and most likely many other ways). You can also supply timing constraints in a UCF file, but I have never managed to get the syntax right!

 

The important bit is that there are no way to implement any logic between the I/O registers and the outside world within the FPGA, so your HDL design needs to reflect this if the "Pack registers into IOB" option is to work. You can see if it has worked by looking at the Pinout report (IIRC) - it has a column named something like "IOB Reg".

 

The other thing you can do is play with the clock-to-data skew settings in the OV7670's registers, or IDELAY and ODELAY settings, but if your signals are not registered in the IOBs then there will always be skew between them, and the amount of skew is likely to change with each build.

 

However, it is most likely not the monitor - but if you want to be sure, just amend the VGA module to output the H counter on one colour channel, and the V counter on another during the visible period. You will get a nice tartan pattern.

 

Mike

Link to comment
Share on other sites

I wouldn't even be so strict as to pack them into IOBs. The run time difference does not matter for RGB.

What I meant is, don't allow combinational logic behind the registers. Make sure that there IS a register in the first place.

 

For example (Verilog), I could try to create a color bar via a combinational assignment

 

wire RGB_R_output = (X > 450 && x < 500).

 

This is likely to glitch as the physical output signal is created using non-clocked, multi-level logic.

 

This, on the other hand

reg RGB_R_output;

wire RGB_R = (X > 450 && x < 500)

always @(posedge clk) RGB_R_output <= RGB_R;

 

is (relatively) safe as now it's guaranteed to change only once, at clock edge.

More on this: http://en.wikipedia.org/wiki/Hazard_%28logic%29

 

The same can happen with off-chip inputs. Forget registering them, and the same signal will sooner or later give two different results within the circuit during the same clock cycle, and you wonder, how could that state machine de-rail.

 

The problem here is maybe that many of the old textbooks aren't written for the FPGA crowd where things should be always synchronous (as a rule of thumb).

Those books start with "and" gates in asynchronous logic, like my first example, then people just use them and aren't aware of the physics behind it. But I could be wrong, haven't seen your code.

Link to comment
Share on other sites

 

The source files are too numerous and too large to post here, so I'm not sure how to get it out here for you more experienced guys to look at and find what I'm missing.  I suppose I could post each one in a separate post...

 

Just zip up the design files to a zip file and attach it to the post.  Click on "More Reply Options" the see the "Attach Files" button.

Link to comment
Share on other sites

I've been playing around with the register settings that Mike had on his project (I copied his settings), and I can change how the image looks.  I have downloaded the datasheet and the Implementation Guide from the links that Mike provided.

 

Looks like I have some reading to do in order to figure out the register settings.

 

What clued me in that this might be the issue was I also noticed that the image is somewhat distorted w.r.t. image height and width.  For example, I know my head isn't that narrow and tall, so I figured that there were register settings that must be setting the scale.  I have made a few changes and these changes definitely changed the image...

 

Let me take a look at these documents and I'll probably post questions regarding what I read.

 

Thanks.

Link to comment
Share on other sites

I've read through the registers and there doesn't seem to be any register settings that address the problem.  The registers apparently just adjust the quality of the image's color, exposure, saturation, etc.  Would you guys take a look at the source file for generating the VGA signal and see if you can figure out where I'm going wrong?  I am doing a 640x480 and I have reduced my clock to 25 MHz.

 

Thanks.

library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;entity vgaGenerator is    Port ( 		clk25	        :	in	STD_LOGIC;		framePixel 	: 	in  	STD_LOGIC_VECTOR (7 downto 0);		vgaRed 		: 	out  	STD_LOGIC_VECTOR (2 downto 0);		vgaGreen 	: 	out  	STD_LOGIC_VECTOR (2 downto 0);		vgaBlue 	: 	out  	STD_LOGIC_VECTOR (2 downto 1);		vgaHSync 	: 	out  	STD_LOGIC;		vgaVSync 	: 	out  	STD_LOGIC;		frameAddress 	: 	out  	STD_LOGIC_VECTOR (14 downto 0));end vgaGenerator;architecture Behavioral of vgaGenerator is	--	Timing Constants		constant	hResolution	:	natural	:=	640;	constant	vResolution	:	natural	:=	480;		constant	hMaximumCount	:	natural	:=	800;	constant	hStartSync	:	natural	:=	655;	constant        hEndSync	:	natural	:=	751;		constant	vMaximumCount	:	natural	:=	525;	constant	vStartSync	:	natural	:=	489;	constant	vEndSync	:	natural	:=	491;		constant	hSyncActive	:	std_logic     :=	'1';	constant	vSyncActive	:	std_logic     :=	'1';		signal	hCounter  :	unsigned(10 downto 0)	:=  (others  =>	'0');	signal	vCounter  :	unsigned(9 downto 0)	:=  (others  =>	'0');	signal	address	  :	unsigned(16 downto 0)	:=  (others  =>	'0');	signal	blank	  :	std_logic		:=  '1';begin	frameAddress	<=	std_logic_vector(address(16 downto 2));		process (clk25)	begin	  if rising_edge(clk25) then	    -- Count the lines and rows	    if hCounter = hMaximumCount - 1 then		hCounter <=	(others	=>	'0');	      if vCounter = vMaximumCount - 1 then		vCounter  <=  (others  =>  '0');	      else	        vCounter  <=  vCounter + 1;	      end if;	    else	      hCounter  <=  hCounter + 1;	    end if;				    if blank = '0' then	      vgaRed    <=  framePixel(7 downto 5);	      vgaGreen  <=  framePixel(4 downto 2);	      vgaBlue	  <=  framePixel(1 downto 0);	    else	      vgaRed    <=  (others  =>  '0');	      vgaGreen  <=  (others  =>  '0');	      vgaBlue	  <=  (others  =>  '0');	    end if;				    if vCounter  >=  vResolution then	      address  <=  (others  =>  '0');	      blank    <=  '1';	    else 	    if hCounter  >=  48 and hCounter  <  640 then	      blank  <=  '0';	    if hCounter = 639 then	      if vCounter(1 downto 0)  /=  "11" then	        address  <=  address - 479;	      else	        address  <=  address + 1;	      end if;	    else	  address  <=  address + 1;	    end if;	  else	    blank  <=  '1';	  end if;      end if;      -- Check to see if in the hSync pulse.  (One has been added to include Frame Buffer Latency)		      if hCounter  >  hStartSync and hCounter  <  hEndSync then	vgaHSync  <=  hSyncActive;      else	vgaHSync  <=  not hSyncActive;      end if;		      -- Check to see if in the vSync pulse.		      if vCounter  >=  vStartSync and vCounter  <  vEndSync then	vgaVSync  <=  vSyncActive;      else	vgaVSync  <=  not vSyncActive;      end if;    end if;  end process;end Behavioral;
Link to comment
Share on other sites

All right, I'm trying to figure this out, and I think I have the problem area narrowed down to this part of the vgaGenerator source.  It looks like what's happening is I have the screen spread too far out in the width and height directions.  If I could get the image to converge, I think I would have a good image.  What I would like to know is how are the lower hCounter, upper hCounter, and vCounter limits derived?  This image is a 640x480 resolution @ 60Hz with the 25 MHz clock.

 

According to some research I've done online, these are the following signal properties for this resolution at this refresh rate:

 

Horizontal Synchronization Pulse Width: 96 pixels

Horizontal Back Porch: 48 pixels

Horizontal Front Porch: 16 pixels

 

Vertical Synchronization Pulse Width: 2 pixels

Vertical Back Porch: 33 pixels

Vertical Front Porch: 10 pixels

 

I have tried as many permutations as I can with this information, and at one point, I had 6 smaller images laid out 3 across the top of the screen and 3 across the bottom.  When I waved my hand in front of the camera, all 6 images changed simultaneously.  It should also be noted that all 6 images were of approximately the same size and pretty much in line with what I would expect a 640x480 window to look like, so I think I got really close, but it was at this point I started suspecting that if I had all 6 images converged together that the image would be correct.  Now I have one image back again, and it looks like all of the pixels are smeared from the top right corner of the window to the bottom left.  The window itself looks about 3 times too wide and about 3 times too tall.

if vCounter  >=  vResolution then  address  <=  (others  =>  '0');  blank    <=  '0';else  if hCounter >= 48 and hCounter  <  656 then    blank  <=  '1';    if hCounter = 655 then      if vCounter(1 downto 0)  /=  "11" then        address  <=  address - 482;      else        address  <=  address + 1;      end if;    else      address  <=  address + 1;    end if;  else    blank  <=  '0';  end if;end if;
Link to comment
Share on other sites

I stringly suspect that your VGA generator is correct, but your RAM addressing is wrong (on either the read or the write from the camera).

 

Why not put a white frame around the visible area, (e.g. if x = [a] or x = [a] + 639 then set red, green and blue to all ones). That way you will know if your VGA is good.

 

Then disable the camera but pre-populate the RAM with a known pattern - see if that works.

 

Then replace the camera module with something that writes a known pattern - see if that works.

 

If that all works. then the camera interface or camera module is at fault.

Link to comment
Share on other sites

Hi Mike.  The image looks different now since I changed the resolution from 800x600 to 640x480.  This required changing the clock from 50 MHz to 25 MHz and it changed all of the constants.  When I had the resolution at 800x600, the image was very pixelated, but I could make out my face and some distinguishing features when I pointed the camera at myself.  Since I've changed the resolution to 640x480 (in an effort to match the camera module's native output resolution), the image is complete garbage now.  You can hopefully see how the image is "smeared" from top right to bottom left and it's entirely too large for a 640x480 resolution.  While adjusting the hCounter and vCounter settings in the snippet supplied in the previous post, this image is noticeably changed, but it doesn't seem to completely converge.  Problem is, I don't know how the hCounter and vCounter values are derived from the information I gathered during my research.  The attached image is for the following values:

 

Minimum hCounter: 48 pixels (Horizontal back porch)

Maximum hCounter: 656 pixels (Horizontal Resolution + Horizontal Front Porch)

vCounter: 482 (Vertical Resolution + Vertical Pulse Width)

 

 

 

 

post-36880-0-67567600-1400471485_thumb.j

Link to comment
Share on other sites

I think this might be your problem

 

address <= address - 482;

 

What should be it is doing is jumping address back to the start of the line, for when the lines repeat. Try 608, as the address gets incremented that many times during a horizontal line (I calc this as 657 - 48).

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.