Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
11 years ago

First verilog project

This is my first work on any FPGA. I have a piece of verilog code I'd like input on. This is a rough start on a module to track an RF signal based on carrier removal and gold code removal. It is assuming input from 2 PLL which it will have to provide feedback to the control logic of.


//correlation module time domain
//------------------------------------------------------
module Correlator_Time_Domain (sampleClock, clk, DIFin, genCodeIn, sinLUT, cosLUT, DLLout, PLLout, DATAout);
 parameter SRsize = 32;
 input sampleClock;
 input clk;
 input DIFin;
 input genCodeIn;
 input sinLUT;
 input cosLUT;
 output real DLLout;
 output real PLLout;
 output DATAout;
 
 reg srIl;
 reg srIe;
 reg srIp;
 reg srQl;
 reg srQe;
 reg srQp;
 reg newData;
 reg Real, Imag;
 reg Ecode, Pcode, Lcode;
 reg Ereal, Preal, Lreal, Eimag, Pimag, Limag;
 integer sumIe, sumIp, sumIl, sumQe, sumQp, sumQl, p;
 
 always @ (posedge sampleClock) //4.096Mhz sampleClock with NCO //may change this
 begin
   Real = DIFin ^ sinLUT;
   Imag = DIFin ^ cosLUT;
   Ereal = Real ^ Ecode;
   Preal = Real ^ Pcode;
   Lreal = Real ^ Lcode;
   Eimag = Imag ^ Ecode;
   Pimag = Imag ^ Pcode;
   Limag = Imag ^ Lcode;
   newData = 1;
 end  
   
 always @ (posedge clk)  //50Mhz main reference
  begin
   if( newData == 1)
   begin
 //shift new samples into shift register for summation
   srIl = srIl;
   srIl = Lreal;
   srIe = srIe;
   srIe = Ereal;
   srIp = srIp;
   srIp = Preal;
   srQl = srQl;
   srQl = Limag;
   srQe = srQe;
   srQe = Eimag;
   srQp = srQp;
   srQp = Pimag;
 //sum shift register values
   for(p = 1; p < SRsize; p = p+1)
    begin
    sumIe = srIe + srIe;
    sumIp = srIp + srIp;
    sumIl = srIl + srIl;
    sumQe = srQe + srQe;
    sumQp = srQp + srQp;
    sumQl = srQl + srQl;
    end
 //compute outputs 
   assign DLLout = ((sumIe*sumIe + sumQe*sumQe)-(sumIl*sumIl + sumQl*sumQl)) / 2; //early - late error value
   assign PLLout = atan(sumQp / sumIp);  //0-90 degree phase error
 //sum nav data //need to add still
   newData = 0;
   end
  end
endmodule

Please let me know if I have syntax errors or other issues

9 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I believe you haven't compiled the code. Quartus will report most errors, especially syntax related.

    Some fatal syntax errors are,

    - "assign" cannot be placed in procedural blocks, such as within the "always"

    - "real" outputs cannot be synthesized

    - atan is likely unsupported

    Non-fatal but violate RTL rules,

    - Procedural block with edged sensitivity list "always @(posedge...)" should use non-blocking assignments, ie <= instead of =

    Non-syntactical but likely not what you want

    - The loop of sumIe...sumQ1 has only the last p index useful. All those p=1 to SRsize-2 are overwritten

    - Integer data type should not be used as nodes. How wide the bus you need for, say sumIe?

    - Direct use of multiplier (*) may not fit well in FPGA without multipliers. It will be large and slow

    Logical errors,

    - The 2 clks are not phase related, the use of "newdata" in 2nd "always" needs proper synchronization.

    - Both clks update "newdata", what do you want it to synthesize? A flip-flop with 2 clks?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I see the logic error in the for loops changed to

    
    //sum shift register values
    			sumIe = srIe;
    			sumIp = srIp;
    			sumIl = srIl;
    			sumQe = srQe;
    			sumQp = srQp;
    			sumQl = srQl; 
    			for(p = 1; p < SRsize; p = p+1)
    				begin
    				sumIe = sumIe + srIe;
    				sumIp = sumIp + srIp;
    				sumIl = sumIl + srIl;
    				sumQe = sumQe + srQe;
    				sumQp = sumQp + srQp;
    				sumQl = sumQl + srQl;
    				end
    

    When I try a compile in Quartus2 vers 13.1 I get this current build errors

    --- Quote Start ---

    Info (12021): Found 3 design units, including 3 entities, in source file basestation1.v

    Info (12023): Found entity 1: search

    Info (12023): Found entity 2: Correlator_Time_Domain

    Info (12023): Found entity 3: NCO

    Info (12021): Found 1 design units, including 1 entities, in source file basestation.bdf

    Info (12023): Found entity 1: Basestation

    Info (12021): Found 1 design units, including 1 entities, in source file codegenerator.v

    Info (12023): Found entity 1: CACODE

    Warning (10236): Verilog HDL Implicit Net warning at Basestation1.v(118): created implicit net for "sine_lookup_output"

    Error (12007): Top-level design entity "Basestation1" is undefined

    Info (144001): Generated suppressed messages file C:/Altera/projects/output_files/Basestation1.map.smsg

    Error: Quartus II 32-bit Analysis & Synthesis was unsuccessful. 1 error, 2 warnings

    Error: Peak virtual memory: 345 megabytes

    Error: Processing ended: Sun Apr 20 22:23:13 2014

    Error: Elapsed time: 00:00:02

    Error: Total CPU time (on all processors): 00:00:01

    Error (293001): Quartus II Full Compilation was unsuccessful. 3 errors, 2 warnings

    --- Quote End ---

    Can I not run 2 operations in the same module? I know the calculation part will take more reference clock cycles than the signal input so I was trying to use "newData" essentially as a boolean value with the always block running at the reference clock checking to see "IF" it was set to 1 by the input block. Can I not work it that way?

    I'm completely new to FPGA design.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    FPGA is not the problem. I think the biggest skill you lack is RTL coding.

    Get a book about Verilog that discusses RTL. It should not be difficult to pick up assumming you have digital logic design background.

    We can go with specific problem here. Without foundation in HDL, I see that it will take a long while to talk through all problems.

    In the mean time, about what you posted,

    - HDL is not software programming. We don't normally code cummulative sumIe in RTL using loop because not all synthesis tools accept this. Not sure about Quartus

    - The Quartus error was about missing Basestation1, which was indicated as top-level design. I guess basestation.bdf is your top-level file. If so, use basestation as top-level design name

    - As of newData, think about what you want the circuit to be, then translate it to code. Remember, HDL does not behave like software. Metastability has be considered transferring signal across asynchrounous clock domains
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I appreciate your responses

    I'm trying to keep it on a logic circuit flow. I'v split some of the functions down into seperate modules. Hopefully I can keep it better organized. It is fully compiling as it is with only a few errors. Pins incomplete IO assignments, I have not run through the pin placement yet. No clocks in design, i still have to figure out how to go about setting those up. Verilog HDL assignment warning at Basestation1.v(75): truncated value with size 4096 to match size of target (4095). I am not certain where I've got it wrong with that one. Those warnings apply to all of my shift registers.

    
    module Correlator_Time_Domain_CA (sampleClock, DIFin, sinLUT, cosLUT, chipIN, IEout, IPout, ILout, QEout, QPout, QLout);
    	input sampleClock;        //4.096Mhz
    	input DIFin, sinLUT, cosLUT;
    	input chipIN;
    	
    	output IEout, IPout, ILout, QEout, QPout, QLout;
    	
    	reg chip;
    	reg result;
    	
    	always @ (posedge sampleClock)
    	begin
    		chip <= {chip, chipIN};        //shift in new early chip to lsb
    		result <= DIFin ^ sinLUT ^ chip;
    		result <= DIFin ^ sinLUT ^ chip;
    		result <= DIFin ^ sinLUT ^ chip;
    		result <= DIFin ^ cosLUT ^ chip;
    		result <= DIFin ^ cosLUT ^ chip;
    		result <= DIFin ^ cosLUT ^ chip;
    		
    			
    	end
    	assign ILout = result;
    	assign IPout = result;
    	assign IEout = result;
    	assign QLout = result;
    	assign QPout = result;
    	assign QEout = result;
    endmodule
    

    Then this is the next step, an adder module

    
    module code_ADDER#  (parameter N = 4096)(IL, IP, IE, QE, QP, QL, sysclk, codeNCOctl, phaseNCOctl);
    	input sysclk;          //50Mhz
    	input IL, IP, IE, QL, QP, QE;
    	output codeNCOctl;
    	output phaseNCOctl;	
    	integer n;
    	reg Iearly;
    	reg Iprompt;
    	reg Ilate;
    	reg Qearly;
    	reg Qprompt;
    	reg Qlate;
    	reg  IEtotal, IPtotal, ILtotal, QEtotal, QPtotal, QLtotal;
    	reg codeAdvance;
    	reg phaseAdvance;
    	
    	always @ (posedge sysclk)
    	begin
    		Iearly  <= {Iearly, IE};
    		Iprompt <= {Iprompt, IP};
    		Ilate   <= {Ilate, IL};
    		Qearly  <= {Qearly, QE};
    		Qprompt <= {Qprompt, QP};
    		Qlate   <= {Qlate, QL};
    		//do averaging
    		IEtotal <= 32'b00000000000000000000000000000000;
    		IPtotal <= 32'b00000000000000000000000000000000;
    		ILtotal <= 32'b00000000000000000000000000000000;
    		QEtotal <= 32'b00000000000000000000000000000000;
    		QPtotal <= 32'b00000000000000000000000000000000;
    		QLtotal <= 32'b00000000000000000000000000000000;
    		for(n=0; n<4096; n= n+1)
    		begin
    			IEtotal <= IEtotal + Iearly;
    			IPtotal <= IPtotal  + Iprompt;
    			ILtotal <= ILtotal + Ilate;
    			QEtotal <= QEtotal + Qearly;
    			QPtotal <= QPtotal + Qprompt;
    			QLtotal <= QLtotal + Qlate;
    		end
    		
    		IPtotal <= IPtotal / N;
    		
    		QEtotal <= QEtotal / N;
    		QPtotal <= QPtotal / N;
    		QLtotal <= QLtotal / N;
    		//calculate control
    		if(IEtotal/N > ILtotal/N)
    		begin
    			codeAdvance <= 2'b11;
    		end
    		else if(IEtotal/N < ILtotal/N)
    		begin
    			codeAdvance <= 2'b01;
    		end
    		else 
    		begin
    			codeAdvance <= 2'b00;
    		end	
    		if(QEtotal * QLtotal > 1)
    		begin
    			phaseAdvance <= 2'b11;  //control bits to advance or retard NCO module
    		end
    		else if (QEtotal < 1)
    		begin
    			phaseAdvance <= 2'b01;
    		end
    		else
    		begin
    			phaseAdvance <= 2'b00;
    		end
    	end
    	assign codeNCOctl = codeAdvance;
    	assign phaseNCOctl = phaseAdvance;
    	
    endmodule
    

    I'm wondering about the adder function. Is there a good model to follow in syncronizing one module to the next when one does accumulate and averaging operations?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    The accumulate and average codes do not work. Maybe you can run simulation to understand better.

    When the same node is updated multiple times in LHS of non-blocking assignments, only the last one takes effect.

    In the code, IEtotal gets reduced to effectively single statement of "IEtotal <= IEtotal + Iearly[4095];". IEtotal is always X without reset. Similarly for the other regs in the loop.

    For summation of IEtotal, you can create intermediate combinatorial adder before feeding it to IEtotal. Break it into 2 "always" blocks. Something like,

    always @(posedge sysclk)

    begin

    ...

    IEtotal <= IEtotal_intermediate;

    ...

    end

    always @*

    begin

    IEtotal_intermediate = 32'h00000000;

    for(n = 0; n < 4096; n = n +1)

    IEtotal_intermediate = IEtotal_intermediate + {31'h00000000, Iearly[n]};

    end
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    So from what I've read so long as the "IEtotal_intermediate" variable is only used inside of the "always" block then use of the blocking "=" operator will work as an accumulator(would you call it a deterministic local variable?) , but if it was also used outside of that always block it would be used as a flip flop as if it had been a non blocking "<=" operator to start with?(non-deterministic?)

    Is it important to also make the distiction you did in the example of + {31 bits , 1 bit input}? Is it just a better notation or will lack of being specific and just adding + 1 bit input cause a problem?

    Thanks again
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Blocking(=) and non-blocking(<=) assignments are different in Verilog.

    Non-blocking assignments have RHS evaluated immediately, but the results are updated at the end of time step. When there are multiple assignments to the same node in the same time step, the last one overrides. The IEtotal depends on uninitialized IEtotal, that makes it undeterministic.

    Blocking assignments are evaluated sequentially. LHS is updated immediately before moving on to evaluate the next statement. Accumulator loop will work.

    In RTL, we do not mix blocking and non-blocking in the same "always" block. This rule is set to ensure consistent behavior comparing simulator with synthesized logic. You can actually change the <= to = in the old code and notice correct simulation behavior, but it will not work on hardware. Most likely a warning is generated during synthesis.

    About the {31'h0000000, Iearly[n]}, it is a good practise to keep both sides of an equation having the same bus width. Also to get rid of annoying warnings.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    So as long as I split them up and do the blocking assignment int intermediate variable seperate it should function.

    
    always @ (posedge sysclk)
    	begin
    		//do averaging
    		IEint = 32'h00000000;
    		IPint = 32'h00000000;
    		ILint = 32'h00000000;
    		QEint = 32'h00000000;
    		QPint = 32'h00000000;
    		QLint = 32'h00000000;
    		for(n=0; n<N; n= n+1)
    		begin
    			IEint = IEint + Iearly;
    			IPint = IPint + Iprompt;
    			ILint = ILint + Ilate;
    			QEint = QEint + Qearly;
    			QPint = QPint + Qprompt;
    			QLint = QLint + Qlate;
    		end
    	end
    	always @ (posedge sysclk)
    	begin
    		IEtotal <= IEint / N;
    		IPtotal <= IPint / N;
    		ILtotal <= ILint / N;
    		QEtotal <= QEint / N;
    		QPtotal <= QPint / N;
    		QLtotal <= QLint / N;
    		//calculate control
    		
    

    My next question would be on an NCO. This is based on a simple sawtooth example I found for fixed freq output. If I feed the output to a sin and cos LUT I should be able to get my mixer values to quadrature split and remove carrier drift from the PRN. Does this code look correct?

    
    module NCOcarrier(sampleclk, NCOout, control);
    	input sampleclk;
    	input  control;     //how to apply control? + or - to IF
    	output  NCOout;				 //control 00 stay, 01 increase, 11 decrease chip rate
    	reg  signalOut = 32'd0;
    	integer IF = 12000000;    //set to center IF
    	
    	
    	always @ (posedge sampleclk)
    	begin
    		if(control == 2'b01)
    		begin
    			rate = rate + 1; //how much to advance or retard
    		end
    		if(control == 2'b11)
    		begin
    			rate = rate - 1;
    		end
    	end
    		
    	always @ (posedge sampleclk)
    	begin
    		signalOut <= signalOut + IF * 64'h100000000 / 50000000;
    	end
    	assign NCOout = signalOut;  //use signalOut as input value for LUT sin and cos
    endmodule
    

    How would I handle roll over of the 32 bit signalOut value? Seems like the value should be kept within 0-90degrees or equivelent to, so as to keep the LUT as small as possible. I'm going to have to think on that one a while.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Of the first piece of code, its sensitivity list should not be edge triggered to model combinatorial logic. Make it "always @*".

    As of the NCO,

    - Both rate and signalOut are uninitialized. Keep in mind that registers are unknowns upon circuit power-up. All branches in the assignments have dependency on old content. Therefore, both registers cannot get meaningful value. You need to find a way to reset or load the registers

    - rate should use <= in assignment

    - The module has only one output, NCOout. Not sure why the rate and control need to be there. They cannot affect NCOout in any way.

    About the truncation of NCOout, it depends how many LUT entries you can afford, effectively the trigo's resolution. You can't be having 2^32 entries. Why not reduce the width of signalOut?