which method to use for clock domain problem?

FPGA projects on this site, or abroad

which method to use for clock domain problem?

Postby DJatPS » Fri Dec 04, 2009 3:18 am

I've been getting a real education in working with FPGA's the last month or so. Perhaps someone could help me work through the way to handle this problem. Hope this isn't too long a post...

Here is a simplified example of what I've been working on. It outputs a byte serially at a slow clock speed.

module doprocess(clk,start,datToSend,done, clkB, sbit);

input clk; // 50 mhz system clock
input start; // signal to reset timer, load byte to shift reg
output done; // set when all bits have been shifted
output clkB; // slow clock synched with serial out
output sbit=shiftreg[7]; // bit being shifted out (msb first)
input [7:0]datToSend; // value to be shifted out

reg [24:0]tmr; // generate slow clock from 50mhz clock

always @(posedge clk)
tmr<=tmr+1;


reg [3:0]cnt; // count to 8 to shift out one byte

// flags when cnt outputs 8 bits
reg cnt_elapsed; always@(posedge clk)cnt_elapsed<= (cnt[3]==1'b1);

assign clkB = tmr[24]; // slower clock

assign done=cnt_elapsed;


reg [7:0] shiftreg;

// notice clocked by slow clock but using start which is from a faster domain
always @(posedge clkB or posedge start)begin

if(start==1'b1)begin
shiftreg<= datToSend;
cnt<=0;
end
else
begin
cnt<=cnt+1;
shiftreg<={shiftreg[6:0],1'b0};
end

end

endmodule


I can load this in a demo board, use switches for data in, LEDS for signals out and it does what it's supposed to, the slow clock flashes on led2, led3 flashes on or off reading off each switch, and led1 comes on when all 8 bits are done:

module top(input clk, //... etc. etc.

doprocess doprocessinst(clk,switches[9],switches[7:0],led1,led2,led3);

endmodule

The challenge for me is how to best implement a controller for this. I have read the links in the crossing domains example project and looked at the examples, but I'm having trouble trying to figure out how to actually implement the techniques in this design. I've tried some of the examples but haven't had success yet. Here is the code that I want to be able to do (of course it won't work because it crosses domains):

module top(.....
.
.
doProcess doprocessinst clk,startprocess, datToSend,processdone,led2,led3);

cmdProcess cmdprocessinst(clk,switches[9],switches[7:0], startprocess, datToSend,processdone,led1);

endmodule


module cmdProcess(
input clk,
input startcmd, // flag from some other source telling us to start sending datinput (i.e. a third possible domain)
input [7:0]datinput, // byte (or bytes) being sent to us to be shifted out
output reg startprocess, // flag to tell doProcess to start shifting
output reg [7:0]datoutput, // byte to send to doProcess
input processdone, // flag from doProcess telling us the byte's done
output reg cmddone // flag we raise after all bytes have been done
);

reg [4:0]fstate;
reg [4:0]reg_fstate;

always @(posedge clk)
fstate <= reg_fstate;

// clock problems 3 domains warring for mastery
always @(posedge clk or negedge startcmd)begin
if(startcmd==1'b0)
reg_fstate<=1;
else
case(fstate)
0:begin
startprocess<=1; // hold this high till ready
end
1:begin
startprocess<=1;
datoutput<=datinput;
reg_fstate<=2;
end
2:begin
startprocess<=0; // let shifter start
if(processdone) begin
reg_fstate<=3;
end
else
reg_fstate<=2;
end
// can repeat here to do multiple bytes
3:begin
cmddone<=1'b1; // tell master we are done
end
endcase
end
endmodule


The problems begin with trying to cross startprocess and processdone
I will keep working on this trying more of the techniques, but if someone could share an example of how to deal with this type of example it would be great. Thanks
DJatPS
 
Posts: 8
Joined: Fri Nov 13, 2009 12:43 am
Location: Arkansas

Postby DJatPS » Fri Dec 04, 2009 7:36 pm

Thought I'd update with how it was going:

I tried to use the 'crossing clock domains-flag' example for catching the flag to start the process. In this example, extreme as it is, you need to do more then raise a flag for the doProcess circuit to see it. You could run it up the flag pole and play revelrie and taps! otherwise the slow clkB just misses it. here is code to capture the startprocess flag that I got to work for me:

reg startToggle; // if we get a start pulse, set startToggle. if we get an ack clear it
always @(posedge clk or posedge start or posedge ack)
if(start==1'b1)
startToggle<=1'b1;
else if(ack==1'b1)
startToggle<=1'b0;


in the main processing loop for the slow clkB, ack is raised after startToggle is acknowledged:

always @(posedge clkB or posedge startToggle)begin
// Oh say does that star spangled banner yet wave?

if(startToggle==1'b1)begin
beginshift<=1'b1;
ack<=1; // "at ease private"
end
else begin
cnt_elapsed<=(cnt[3]==1'b1); // note moved this here, it should have been in this time domain all along.

if(beginshift==1'b1) begin // this was added to allow the shift to start
cnt<=0; // in synch with the clock so first pulse
shiftreg<=datToSend; // same duration as all others
beginshift<=1'b0;
end
else
begin
ack<=0;
cnt<=cnt+1;
shiftreg<={shiftreg[6:0],1'b0};

end
end


I was able to use two register clocks to move the other signals in to the controller's clock domain as shown in the crossing clock domains example:

always@(posedge clk)startcmdr[0]<=startcmd;
always@(posedge clk)startcmdr[1]<=startcmdr[0];
wire synchedStartCmd=startcmdr[1];

// synch processdone coming in from doprocess, will be looooong relative to clk

reg [1:0]procdoner;
always@(posedge clk)procdoner[0]<=processdone;
always@(posedge clk)procdoner[1]<=procdoner[0];
wire synchedprocdone = procdoner[1];



This all compiled and passed the timing analysis. I'm working on the data lines now...
DJatPS
 
Posts: 8
Joined: Fri Nov 13, 2009 12:43 am
Location: Arkansas

Re: which method to use for clock domain problem?

Postby barawn » Wed Dec 09, 2009 8:46 pm

DJatPS wrote:reg [24:0]tmr; // generate slow clock from 50mhz clock

always @(posedge clk)
tmr<=tmr+1;

...

assign clkB = tmr[24]; // slower clock

...

always @(posedge clkB or posedge start)begin



Ack, doom, doom, doom.

You really, really don't want to do this sort of thing in an FPGA. The thing is, you don't really have two clock domains here. You've got one clock domain. The second is derived purely from the first, so the only question is "how do I run something in 1 clock domain only some of the time?"

That should provide the hint already: clock enables. The way you really want to do this is:

Code: Select all
// Parametrized clock divide.
// clkB_en is a flag that appears only once every CLKB_DIVIDE
// cycles.
localparam [24:0] CLKB_DIVIDE = 24'h800000;

reg [24:0] tmr;
wire clkB_en;

assign clkB_en = (tmr == CLKB_DIVIDE -1);

always @(posedge clk) begin
    if (clkB_en) tmr <= 0;
    else tmr <= tmr + 1;
end

// note that you should also consider whether start
// has to be asynchronous, or whether it can be
// synced with clk and passed that way.
always @(posedge clk or posedge start) begin
  if (start) // do start stuff
  else if (clkB_en) // do stuff on clkB
end


You always, always, always want to minimize the number of clock domains as much as possible.

Side note: I would recommend putting the clkB flag generator in its own module. There are many ways to generate a slower clock enable flag from a larger one, each with varying advantages. Using just "assign clkB_en = tmr[24]" works but generates a flag every 2^24 + 1 cycles. For a 50 MHz clock dividing this way is fine: for a faster system clock, you might have to do a multi-stage divide since the adder could get slow. Xilinx's SRL16s can do this pretty well - take a look at http://www.xilinx.com/support/documentation/white_papers/wp271.pdf for details. The SRL16 would also be cheaper, but it's a pain to implement (I should really try to make an automagic module which does this...).

The reasons I really stress "one clock domain only" are:

1) the pain in the neck that clock-domain crossing creates
2) there's no need
3) there aren't that many global clock resources in an FPGA
4) you can't simulate clock domain crossing perfectly.

I should note that your method will work, absolutely... until weird things start happening, which you then might try to fix with timing constraints, until other weird things start happening... etc., etc.

It's always a pain to redo a design when it's working, but I would really recommend it.
barawn
 
Posts: 4
Joined: Sat Jul 11, 2009 10:47 am

Postby DJatPS » Wed Dec 09, 2009 10:45 pm

Ah, I see some light here! Thanks. It seems so logical to let a slower clock control always loop to count, but this is really simple. I'll try it right now...
DJatPS
 
Posts: 8
Joined: Fri Nov 13, 2009 12:43 am
Location: Arkansas

Postby DJatPS » Thu Dec 10, 2009 8:54 pm

A question, how do you get a 50% duty cycle clock out of this? This makes a nice one cycle flag that I can use to detect the edge of the clock, byte what about the slower clock. By the way, I'm really trying to get two clocks switch selectable. one clock around 100-200khz (tmr[7]) and one about 25 mhz tmr[0]. But if I use the clock the divider it's harder to figure out the waveform.
DJatPS
 
Posts: 8
Joined: Fri Nov 13, 2009 12:43 am
Location: Arkansas

Postby DJatPS » Fri Dec 11, 2009 3:33 am

Here is a modification that uses the original approach of generating the clock but uses a one clock cycle flag to give us the desired edge of the slow clock:

Code: Select all
module ClockGen( // generates two clocks based on slowmode flag, run all the time
input clk, // system clock
input slowmode, // 100-200khz, or 25mhz
output clkB, // slow clock out
output reg clk_flag  // desired edge of serial clock, timed in our system clk domain
);

reg [7:0] tmr;

assign clkB = (slowmode) ? tmr[7] : tmr[0];

// look for the business edge of the clock ie. rising (01) or falling (10)
reg [2:0]clkr;always @(posedge clk)clkr<={clkr[1:0],clkB};

always@(posedge clk)
   clk_flag = (clkr[2:1]==2'b10); // need falling edge of clock for this app, change to 2'b01 if you need rising edge
      
always @(posedge clk)
   tmr<=tmr+1; // free running timer
endmodule


And here is a routine that uses the generated flag to tell when to shift the data.
Code: Select all
module shiftbyte(  // serial shift byte out.  It's synched to falling edge of serial clock
input clk,
input [7:0]byteOut, // byte to send
input we, // signal to start sending byte
output reg busy, // response back saying we are processing byte, go low when done
input clk_flag, // serial flag edge clock, set to give us falling edges, do work when this flag is high
input sd_enable, // high when serial data can be shifted out
output serialOut // serial stream synched to serial clock

);

   assign serialOut = shiftreg[7]; // output msb first
   
   reg[3:0]cnt;  //counts to 8, once for each bit
   reg [3:0]reg_cnt; // gives us a clock delay
   reg [7:0]shiftreg; // holds byte being shifted
   reg startshift; // flag indicating we are starting
   reg reg_startshift;  // one clock delay
   
   reg [7:0]reg_shiftreg;
   
   always @(posedge clk)begin
      cnt<=reg_cnt;
      startshift<=reg_startshift;
      shiftreg<=reg_shiftreg;
   end
      
   always @(posedge clk)  // "busy" signals the clockenable circuit to go live...
      if(startshift==1'b1)begin
         busy<=1'b1;
      end
      else
         if(sd_enable) // ... or to go offline  sd-enable is the response back from the clock enabler circuit
            if(cnt==4'b0111)
               busy<=1'b0;
   
               
   always @(posedge clk or posedge we)begin
      if(we==1'b1) begin  // signal to start
         reg_startshift<=1'b1; // set our internal flag
         reg_cnt<=0; // set counter to 0
         reg_shiftreg<=byteOut; // load byte to shift out
      end
      else if(clk_flag==1'b1) // on negedge of serial clock do:
         if(sd_enable==1'b1)begin // if we are online do:
            reg_startshift<=1'b0; // clear our internal flag so busy can reset
            reg_cnt<=cnt + 1;  //... when count gets to 8
            reg_shiftreg<={reg_shiftreg[6:0],1'b0}; // shift the next bit out for next clock
                                           // target device will read the bit on rising edge of serial clock
         end

   end
endmodule   
   


It used one more routine to enable or disable the serial clock to give us nice clean waveforms starting and ending at 0
Code: Select all
module ClockEnable(
input clk,
input en,
input serial_clk,
output  reg sd_enable
);

// we are letting the clock run all the time
// but only putting it out on the serial line
// when we are clocking out data
// we need to make a nice clean waveform,
// can't start or stop in middle of a clock

// identify rising and falling edges of serial clock
reg [2:0]flagin;  // gets a streaming video (youtube?) of the clock waveform
               // e.g. 000111000111000 etc.
always @(posedge clk) flagin <= {flagin[1:0], &serial_clk};



always @(posedge clk)
   if(en==1'b1) begin  // did someone signal us to enable the clock?
      if(flagin[2:1]==2'b01) // wait until we see raising edge in stream
         sd_enable<=1'b1; // turn on only when slow clock is rising
   end
   else  // did someone signal us to disable the clock?
      if(flagin[2:1]==2'b10) //wait until we see a falling edge in the stream
         sd_enable<=1'b0; // turns off when clock has completed cycle
endmodule


This seems to be working and keeps me away from clocking an always loop using the serial clock like I did at first. The code is more logical this way. I appreciate your help. Still wondering though if there are better ways to generate the clock? or any other comments on this code?
DJatPS
 
Posts: 8
Joined: Fri Nov 13, 2009 12:43 am
Location: Arkansas


Return to General projects