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

Subversion Repositories bilinear_demosaic

[/] [bilinear_demosaic/] [trunk/] [rtl/] [verilog/] [bilinearDemosaic.v] - Rev 2

Compare with Previous | Blame | View Log

/*-----------------------------------------------------------------------------
 
								Bilinear Demosaic
 
							Author: David Kronstein
 
 
 
Copyright 2011, David Kronstein, and individual contributors as indicated
by the @authors tag.
 
This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
 
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
 
You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
 
 
-------------------------------------------------------------------------------
 
Provides demosaicing of a streamign video source.
 
 
Bayer pattern codes for input bayerPattern:
0=R G
  G B
 
1=B G
  G R
 
2=G R
  B G
 
3=G B
  R G
 
-------------------------------------------------------------------------------
 
Revisions
 
V1.0.0	Nov 16 2012		Initial Release		David Kronstein
 
 
 
*/
`default_nettype none
 
module bilinearDemosaic #(
//---------------------------Parameters----------------------------------------
parameter	DATA_WIDTH =			8,		//Width of input/output data
parameter	X_RES_WIDTH =			11,		//Widths of input/output resolution control signals
parameter	Y_RES_WIDTH =			11,
parameter	BUFFER_SIZE =			4,		//Depth of RFIFO
//---------------------Non-user-definable parameters----------------------------
parameter	BUFFER_SIZE_WIDTH =		((BUFFER_SIZE+1) <= 2) ? 1 :	//wide enough to hold value BUFFER_SIZE + 1
									((BUFFER_SIZE+1) <= 4) ? 2 :
									((BUFFER_SIZE+1) <= 8) ? 3 :
									((BUFFER_SIZE+1) <= 16) ? 4 :
									((BUFFER_SIZE+1) <= 32) ? 5 :
									((BUFFER_SIZE+1) <= 64) ? 6 : 7
)(
//---------------------------Module IO-----------------------------------------
//Clock and reset
input wire						clk,
input wire						rst,
 
//User interface
//Video Input
input wire [DATA_WIDTH-1:0]		dIn,
input wire						dInValid,
output wire						nextDin,
input wire						start,
 
//Video Output
output reg [DATA_WIDTH-1:0]
								rOut,
output reg [DATA_WIDTH-1:0]
								gOut,
output reg [DATA_WIDTH-1:0]
								bOut,
output reg						dOutValid,			//latency of 1 clock cycle after nextDout is asserted
input wire						nextDout,
 
//Control
input wire [1:0]				bayerPattern,		//Controls which of four bayer pixel patterns is used
input wire [X_RES_WIDTH-1:0]	xRes,				//Resolution of input data minus 1
input wire [Y_RES_WIDTH-1:0]	yRes
 
);
//-----------------------Internal signals and registers------------------------
reg								advanceRead;
 
wire [DATA_WIDTH-1:0]			readData0;
wire [DATA_WIDTH-1:0]			readData1;
wire [DATA_WIDTH-1:0]			readData2;
 
wire [X_RES_WIDTH-1:0]			readAddress;
 
reg 							readyForRead;		//Indicates two full lines have been put into the buffer
reg [Y_RES_WIDTH-1:0]			outputLine;			//which output video line we're on
reg [X_RES_WIDTH-1:0]			outputColumn;		//which output video column we're on
wire [BUFFER_SIZE_WIDTH-1:0] 	fillCount;			//Numbers used rams in the ram fifo
reg								dOutValidInt;
reg								fillPipeline;
reg								fillPipeline_1;
reg [2:0]						fillPipelineCount;
 
wire 							allDataWritten;		//Indicates that all data from input has been read in
reg 							readState;
 
//States for read state machine
parameter RS_START =		1'b0;
parameter RS_READ_LINE =	1'b1;
 
//Read state machine
//Controls the RFIFO(ram FIFO) readout and generates output data valid signals
always @ (posedge clk or posedge rst or posedge start)
begin
	if(rst | start)
	begin
		outputLine <= 0;
		outputColumn <= 0;
		readState <= RS_START;
		dOutValidInt <= 0;
		advanceRead <= 0;
		fillPipeline <= 0;
		fillPipeline_1 <= 0;
		fillPipelineCount <= 0;
	end
	else
	begin
		case (readState)
 
			RS_START:
			begin
				if(readyForRead)
				begin
					readState <= RS_READ_LINE;
					dOutValidInt <= 1;
					fillPipeline <= 1;
					fillPipelineCount <= 4;
				end
			end
 
			RS_READ_LINE:
			begin
				if(nextDout && dOutValidInt || fillPipeline)
				begin
					if(outputColumn == xRes)
					begin //On the last input pixel of the line
 
						advanceRead <= 1;
						if(fillCount < (3 + 1))		//If the RFIFO doesn't have enough data, stop reading it out (+1 to account for fill level after advancing the RRB)
							dOutValidInt <= 0;
 
						outputColumn <= 0;
						outputLine <= outputLine + 1;
					end
					else
					begin
						//Advance the output pixel selection values
						outputColumn <= outputColumn + 1;
						advanceRead <= 0;
					end
				end
				else //else from if(nextDout && dOutValidInt || fillPipeline)
				begin
					advanceRead <= 0;
				end
 
				//Once the RFIFO has enough data, let data be read from it.
				if(fillCount >= 3 && dOutValidInt == 0 || allDataWritten)
				begin
					if(!advanceRead)
					begin
						dOutValidInt <= 1;
					end
				end
 
				//Counter for pipeline fill time
				if(fillPipelineCount > 0)
				begin
					fillPipelineCount <= fillPipelineCount - 1;
				end
				else
				begin
					fillPipeline <= 0;
				end
 
				fillPipeline_1 <= fillPipeline;
 
			end//state RS_READ_LINE:
		endcase
 
	end
end
 
assign readAddress = outputColumn;
 
//Generate dOutValid signal, delayed to account for delays in data path
always @(posedge clk or posedge rst)
begin
	if(rst)
	begin
		dOutValid <= 0;
	end
	else
	begin
		dOutValid <= nextDout && dOutValidInt;
	end
end
 
 
wire					advanceWrite;
reg [1:0]				writeState;
reg [X_RES_WIDTH-1:0]	writeColCount;
reg [Y_RES_WIDTH-1:0]	writeRowCount;
reg						enableNextDin;
reg						forceRead;
 
//Write state machine
//Controls writing scaler input data into the RFIFO
parameter	WS_START = 0;
parameter	WS_DISCARD = 1;
parameter	WS_READ = 2;
parameter	WS_DONE = 3;
 
//Control write and address signals to write data into ram FIFO
always @ (posedge clk or posedge rst or posedge start)
begin
	if(rst | start)
	begin
		writeState <= WS_START;
		enableNextDin <= 0;
		readyForRead <= 0;
		writeRowCount <= 0;
		writeColCount <= 0;
		forceRead <= 0;
	end
	else
	begin
		case (writeState)
 
			WS_START:
			begin
				enableNextDin <= 1;
				writeState <= WS_READ;
			end
 
			WS_READ:
			begin
				if(dInValid & nextDin)
				begin
					if(writeColCount == xRes)
					begin	//Occurs on the last pixel in the line
 
						//Once writeRowCount is >= 3, data is ready to start being output.
						if(writeRowCount[1:0] == 2'h2)
							readyForRead <= 1;
 
						if(writeRowCount == yRes)	//When all data has been read in, stop reading from input.
						begin
							writeState <= WS_DONE;
							enableNextDin <= 0;
							forceRead <= 1;
						end
 
						writeColCount <= 0;
						writeRowCount <= writeRowCount + 1;
					end
					else
					begin
						writeColCount <= writeColCount + 1;
					end
				end
			end
 
			WS_DONE:
			begin
				//do nothing, wait for reset
			end
 
		endcase
	end
end
 
//Masks to disable blending of invalid data (when at edges of image and no data is available for some pixels)
wire leftMask, rightMask, topMask, bottomMask;
 
wire leftMask_1 =	~(outputColumn == 0);
wire rightMask_1 =	~(outputColumn == xRes);
wire topMask_1 =	~(outputLine == 0);
wire bottomMask_1 =	~(outputLine == yRes);
 
//delay mask signals as required
registerDelay #(
	.DATA_WIDTH( 4 ),
	.STAGES( 3 )
) rd_edgeMask (
	.clk( clk ),
	.rst( rst | start ),
	.enable( dOutValid || fillPipeline ),
	.d( {leftMask_1, rightMask_1, topMask_1, bottomMask_1} ),
	.q( {leftMask, rightMask, topMask, bottomMask} )
	);
 
 
reg [DATA_WIDTH-1:0]	pixel [2:0][2:0];    //[y, x] pixel da
wire [DATA_WIDTH-1:0]	pixelMasked [2:0][2:0];    //[y, x]
/*
Pixel data format
 
pixel[0][0]	pixel[0][1]	pixel[0][2]	
pixel[1][0]	pixel[1][1]	pixel[1][2]	
pixel[2][0]	pixel[2][1]	pixel[2][2]	
 
*/
 
always @ (posedge clk or posedge rst or posedge start)
begin
   if(rst | start)
   begin
       pixel[0][0] <= 0;
       pixel[0][1] <= 0;
       pixel[0][2] <= 0;
       pixel[1][0] <= 0;
       pixel[1][1] <= 0;
       pixel[1][2] <= 0;
       pixel[2][0] <= 0;
       pixel[2][1] <= 0;
       pixel[2][2] <= 0;
   end
   else
   begin
	   if( dOutValid || fillPipeline_1 )
	   begin
		   pixel[0][2] <= readData0;	//Upper line
		   pixel[0][1] <= pixel[0][2];
		   pixel[0][0] <= pixel[0][1];
 
		   pixel[1][2] <= readData1;	//Middle line
		   pixel[1][1] <= pixel[1][2];
		   pixel[1][0] <= pixel[1][1];
 
		   pixel[2][2] <= readData2;	//Lower line
		   pixel[2][1] <= pixel[2][2];
		   pixel[2][0] <= pixel[2][1];
		end	   
	end
end
 
//Apply masking so invalid data at the edge of the image is not used
assign pixelMasked[0][0] = pixel[0][0] & {DATA_WIDTH{leftMask}} & {DATA_WIDTH{topMask}};
assign pixelMasked[0][1] = pixel[0][1] & {DATA_WIDTH{topMask}};
assign pixelMasked[0][2] = pixel[0][2] & {DATA_WIDTH{rightMask}} & {DATA_WIDTH{topMask}};
assign pixelMasked[1][0] = pixel[1][0] & {DATA_WIDTH{leftMask}};
assign pixelMasked[1][1] = pixel[1][1];
assign pixelMasked[1][2] = pixel[1][2] & {DATA_WIDTH{rightMask}};
assign pixelMasked[2][0] = pixel[2][0] & {DATA_WIDTH{leftMask}} & {DATA_WIDTH{bottomMask}};
assign pixelMasked[2][1] = pixel[2][1] & {DATA_WIDTH{bottomMask}};
assign pixelMasked[2][2] = pixel[2][2] & {DATA_WIDTH{rightMask}} & {DATA_WIDTH{bottomMask}};
 
wire [2:0]	sidesMasked = ~leftMask + ~rightMask + ~topMask + ~bottomMask;	//Number of sides masked, either 0, 1 or 2. Used for selecting how to divide during averaging
reg [2:0]	sidesMaskedReg;
 
/*
Perform demosaic blending
All possible blend modes are computed simultaneously, and
the proper ones are selected based on which color filter is being worked on
 
Blend modes:
blend1 = +	(average of four pixels N S E and W)
blend2 = X	(average of four pixels NE SE SW and NW)
blend3 = --	(average of pixels E and W)
blend4 = |	(average of pixels N and S)
blend5 = straight through
*/
 
wire [DATA_WIDTH+1:0]	blend1Sum_1 = pixelMasked[1][0] + pixelMasked[1][2] + pixelMasked[0][1] + pixelMasked[2][1];
reg [DATA_WIDTH+1:0]	blend1SumOver3, blend1Sum;
reg [DATA_WIDTH+1:0]	blend1, blend2, blend3, blend4, blend5, blend2_1, blend3_1, blend4_1, blend5_1;
 
always @ (posedge clk or posedge rst or posedge start)
begin
	if(rst | start)
	begin
		blend1SumOver3 <= 0;
		blend1Sum <= 0;
		blend1 <= 0;
		blend2 <= 0;
		blend3 <= 0;
		blend4 <= 0;
		sidesMaskedReg <= 0;
	end
	else
	begin
		if( dOutValid || fillPipeline_1 )
		begin
			blend1SumOver3 <= (blend1Sum_1 >> 2) + (blend1Sum_1 >> 4) + (blend1Sum_1 >> 6) + (blend1Sum_1 >> 10);	//Constant multiply by 1/3 (approximate, but close enough)
			blend1Sum <= blend1Sum_1;
			blend1 <= ((sidesMaskedReg == 0) ? blend1Sum >> 2 : (sidesMaskedReg == 1) ? blend1SumOver3 : blend1Sum >> 1);	// divide by 4, 3, 2
 
			blend2_1 <= (pixelMasked[0][0] + pixelMasked[2][2] + pixelMasked[0][2] + pixelMasked[2][0]) >> ((sidesMasked == 0) ? 2 : (sidesMasked == 1) ? 1 : 0);	// divide by 4, 2, 1
			blend3_1 <= (pixelMasked[1][0] + pixelMasked[1][2]) >> ((!leftMask || !rightMask) ? 0 : 1);	//divide by 2, 1
			blend4_1 <= (pixelMasked[0][1] + pixelMasked[2][1]) >> ((!topMask || !bottomMask) ? 0 : 1);	//divide by 2, 1
			blend5_1 <= pixelMasked[1][1];	//Straight through
 
			blend2 <= blend2_1;
			blend3 <= blend3_1;
			blend4 <= blend4_1;
			blend5 <= blend5_1;
 
			sidesMaskedReg <= sidesMasked;
		end
 
	end
end
 
 
/*
Bayer pattern codes:
0=R G
  G B
 
1=B G
  G R
 
2=G R
  B G
 
3=G B
  R G
 
  Pixel codes
  0 = R, 1 = G with B above, 2 = G with R above, 3 = B
*/
reg [1:0] pixel0; 
reg [1:0] pixel1; 
reg [1:0] pixel2; 
reg [1:0] pixel3; 
 
always @(*)
begin
	case(bayerPattern)
	0:
	begin
		pixel0 = 0; 
		pixel1 = 1; 
		pixel2 = 2; 
		pixel3 = 3; 
	end
 
	1:
	begin
		pixel0 = 3; 
		pixel1 = 2; 
		pixel2 = 1; 
		pixel3 = 0; 
	end
 
	2:
	begin
		pixel0 = 1; 
		pixel1 = 0; 
		pixel2 = 3; 
		pixel3 = 2; 
	end
 
	3:
	begin
		pixel0 = 2; 
		pixel1 = 3; 
		pixel2 = 0; 
		pixel3 = 1; 
	end
	endcase
end
 
wire [1:0]	quadPosition = {outputLine[0], outputColumn[0]};
wire [1:0]	blendModeSelect_1 =	quadPosition == 0 ? pixel0 :
								quadPosition == 1 ? pixel1 :
								quadPosition == 2 ? pixel2 :
													pixel3;
wire [1:0]	blendModeSelect;
 
//Delay blend mode
registerDelay #(
	.DATA_WIDTH( 2 ),
	.STAGES( 5 )
) rd_blendMode (
	.clk( clk ),
	.rst( rst | start ),
	.enable( dOutValid || fillPipeline_1 ),
	.d( blendModeSelect_1 ),
	.q( blendModeSelect )
	);
 
//Select proper blend mode for each R G and B output
always @ (posedge clk or posedge rst or posedge start)
begin
	if(rst | start)
	begin
		rOut <= 0;
		gOut <= 0;
		bOut <= 0;
	end
	else
	begin
		if( dOutValid || fillPipeline_1 )
		begin
			case(blendModeSelect)
			0:	//Red filter
			begin
				rOut <= blend5;	// Straight through
				gOut <= blend1;	// +
				bOut <= blend2;	// X
			end
 
			1:	//Green filter with blue above/below
			begin
				rOut <= blend3;	// --
				gOut <= blend5;	// Straight through
				bOut <= blend4;	// |
			end
 
			2:	//Green filter with red above/below
			begin
				rOut <= blend4;	// |
				gOut <= blend5;	// Straight through
				bOut <= blend3;	// --
			end
 
			3:	//Blue filter
			begin
				rOut <= blend2;	// X
				gOut <= blend1;	// +
				bOut <= blend5;	// Straight through
			end
			endcase
		end
	end
end
 
 
//Advance write whenever we have just written a valid line (discardInput == 0)
//Generate this signal one earlier than discardInput above that uses the same conditions, to advance the buffer at the right time.
assign advanceWrite =	(writeColCount == xRes) & dInValid & nextDin;
assign allDataWritten = writeState == WS_DONE;
assign nextDin = (fillCount < BUFFER_SIZE) & enableNextDin;
 
ramFifo #(
	.DATA_WIDTH( DATA_WIDTH ),
	.ADDRESS_WIDTH( X_RES_WIDTH ),	//Controls width of RAMs
	.BUFFER_SIZE( BUFFER_SIZE )		//Number of RAMs
) ramRB (
	.clk( clk ),
	.rst( rst | start ),
	.advanceRead( advanceRead ),
	.advanceWrite( advanceWrite ),
 
	.writeData( dIn ),		
	.writeAddress( writeColCount ),
	.writeEnable( dInValid & nextDin & enableNextDin ),
	.fillCount( fillCount ),
 
	.readData0( readData0 ),
	.readData1( readData1 ),
	.readData2( readData2 ),
	.readAddress( readAddress )
);
 
endmodule	//bilinearDemosaic
 
 
 
//---------------------------Ram FIFO (RFIFO)-----------------------------
//FIFO buffer with rams as the elements, instead of data
//One ram is filled, while three others are simultaneously read out.
module ramFifo #(
	parameter DATA_WIDTH = 8,
	parameter ADDRESS_WIDTH = 8,
	parameter BUFFER_SIZE = 3,
	parameter BUFFER_SIZE_WIDTH =	((BUFFER_SIZE+1) <= 2) ? 1 :	//wide enough to hold value BUFFER_SIZE + 1
									((BUFFER_SIZE+1) <= 4) ? 2 :
									((BUFFER_SIZE+1) <= 8) ? 3 :
									((BUFFER_SIZE+1) <= 16) ? 4 :
									((BUFFER_SIZE+1) <= 32) ? 5 :
									((BUFFER_SIZE+1) <= 64) ? 6 : 7
)(
	input wire 						clk,
	input wire 						rst,
	input wire						advanceRead,	//Advance selected read RAM by one
	input wire						advanceWrite,	//Advance selected write RAM by one	
 
	input wire [DATA_WIDTH-1:0]		writeData,
	input wire [ADDRESS_WIDTH-1:0]	writeAddress,
	input wire						writeEnable,
	output reg [BUFFER_SIZE_WIDTH-1:0]
									fillCount,
 
	output wire [DATA_WIDTH-1:0]	readData0,		//Read from deepest RAM (earliest data), at readAddress
	output wire [DATA_WIDTH-1:0]	readData1,		//Read from second deepest RAM (second earliest data), at readAddress
	output wire [DATA_WIDTH-1:0]	readData2,		//Read from third deepest RAM (third earliest data), at readAddress
	input wire [ADDRESS_WIDTH-1:0]	readAddress
);
 
reg [BUFFER_SIZE-1:0]				writeSelect;
reg [BUFFER_SIZE-1:0]				readSelect;
 
//Read select ring register
always @(posedge clk or posedge rst)
begin
	if(rst)
		readSelect <= {1'b1, {(BUFFER_SIZE-1){1'b0}}}; //Mod for demosaic, normally 1
	else
	begin
		if(advanceRead)
		begin
			readSelect <= {readSelect[BUFFER_SIZE-2 : 0], readSelect[BUFFER_SIZE-1]};
		end
	end
end
 
//Write select ring register
always @(posedge clk or posedge rst)
begin
	if(rst)
		writeSelect <= 1;
	else
	begin
		if(advanceWrite)
		begin
			writeSelect <= {writeSelect[BUFFER_SIZE-2 : 0], writeSelect[BUFFER_SIZE-1]};
		end
	end
end
 
wire [DATA_WIDTH-1:0] ramDataOut [2**BUFFER_SIZE-1:0];
 
//Generate to instantiate the RAMs
generate
genvar i;
	for(i = 0; i < BUFFER_SIZE; i = i + 1)
		begin : ram_generate
 
			ramDualPort #(
				.DATA_WIDTH( DATA_WIDTH ),
				.ADDRESS_WIDTH( ADDRESS_WIDTH )
			) ram_inst_i(
				.clk( clk ),
 
				//Port A is written to, port B is read from
				.addrA( writeAddress ),
				.dataA( writeData ),													
				.weA( (writeSelect[i] == 1'b1) ? writeEnable : 1'b0 ),
				.qA(  ),
 
				.addrB( readAddress ),
				.dataB( 0 ),
				.weB( 1'b0 ),
				.qB( ramDataOut[2**i] )
			);
		end
endgenerate
 
//Select which ram to read from
wire [BUFFER_SIZE-1:0]	readSelect0 = readSelect;
wire [BUFFER_SIZE-1:0]	readSelect1 = (readSelect << 1) | readSelect[BUFFER_SIZE-1];
wire [BUFFER_SIZE-1:0]	readSelect2 = (readSelect << 2) | readSelect[BUFFER_SIZE-1:BUFFER_SIZE-2];
 
//Steer the output data to the right ports
assign readData0 = ramDataOut[readSelect0];
assign readData1 = ramDataOut[readSelect1];
assign readData2 = ramDataOut[readSelect2];
 
 
//Keep track of fill level
always @(posedge clk or posedge rst)
begin
	if(rst)
	begin
		fillCount <= 1;		//Mod for demosaic, normally 0. The first line has to come out of readData1, the invalid data from readData0 will be masked
	end
	else
	begin
		if(advanceWrite)
		begin
			if(advanceRead)
				fillCount <= fillCount;
			else
				fillCount <= fillCount + 1;
		end
		else
		begin
			if(advanceRead)
				fillCount <= fillCount - 1;
			else
				fillCount <= fillCount;
		end
	end
end
 
endmodule //ramFifo
 
 
//Dual port RAM
module ramDualPort #(
	parameter DATA_WIDTH = 8,
	parameter ADDRESS_WIDTH = 8
)(
	input wire [(DATA_WIDTH-1):0] dataA, dataB,
	input wire [(ADDRESS_WIDTH-1):0] addrA, addrB,
	input wire weA, weB, clk,
	output reg [(DATA_WIDTH-1):0] qA, qB
);
 
	// Declare the RAM variable
	reg [DATA_WIDTH-1:0] ram[2**ADDRESS_WIDTH-1:0];
 
	//Port A
	always @ (posedge clk)
	begin
		if (weA) 
		begin
			ram[addrA] <= dataA;
			qA <= dataA;
		end
		else 
		begin
			qA <= ram[addrA];
		end 
	end 
 
	//Port B
	always @ (posedge clk)
	begin
		if (weB) 
		begin
			ram[addrB] <= dataB;
			qB <= dataB;
		end
		else 
		begin
			qB <= ram[addrB];
		end 
	end
 
endmodule //ramDualPort
 
`default_nettype wire
 

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.