Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Propper clock generation for SPI protocol

Status
Not open for further replies.

Ironlord

Member level 3
Joined
Oct 16, 2018
Messages
63
Helped
1
Reputation
2
Reaction score
1
Trophy points
8
Activity points
713
Hello.

I want to implement the SPI protocol in VHDL over an FPGA.
My question here is quite simple, I want an option in order to decide which frequency to use. For example, if I have a fast device as an screen an a slow device as a sensor I need to communicate them with different frequencies.
As my FPGA works @50MHz I thought it would be a great idea to make a clock divider, so I would work with 50, 25, 10, 5, 2 and 1Mhz.

How is the best way to implement them? I could define some values and count them, but I also can have 6 clocks at the same time.
This last option would be something like this:

Code:
Process(CLK50) is
	If(rising_edge(CLK50))then
		CLK25<=NOT(CLK25)
	End if;
End Process;

Process(CLK25) is
	If(rising_edge(CLK25))then
		CLK10<=NOT(CLK10)
	End if;
End Process;

So I would have 6 process. I guess this is not the best option, but I'm not sure if generating clocks from counters is such a good idea.
 

25/2 is hardly giving 10 MHz. Apart from this trivial calculation error, your ripple carry divider is about the worst method to generate a clock.

You should consider a simple scheme with a single clock domain (e.g. 50 MHz) and clock enables of different speed generating the output signals. This means, no edge sensitive condition will use a different clock than 50 MHz.

A general comment regarding your intention, it's rather unusual to switch SCK frequency of a SPI bus to serve devices with different speed capability. I presume it's not even permitted in the datasheets.
 

Would something like this be correct or is there a better way? I ask because, in the other thread I opened, I had problems with clock generations, and I want to do it right this time.

Code:
	Clock25: process(CLK_50)
	begin
		if(rising_edge(CLK_50))then
			if(en_Clk25='1')then
				CLK_25<=NOT(CLK_25);
			end if;
		end if;
	end process;
	
	Clock10: process(CLK_50)
	begin
		if(rising_edge(CLK_50))then
			if(en_Clk10='1')then
				if(count10<half10)then
					CLK_10<='1';
					count<=count+'1';
				else
					CLK_10<='1';
					count<=count+'1';
					if(count=max10)then
						count<=(others => '0');
					end if;
				end if;
			end if;
		end if;
	end process;
	
	Clock5: process(CLK_50)
	begin
		if(rising_edge(CLK_50))then
			if(en_Clk5='1')then
				if(count5<half5)then
					CLK_5<='1';
					count<=count+'1';
				else
					CLK_5<='1';
					count<=count+'1';
					if(count=max5)then
						count<=(others => '0');
					end if;
				end if;
			end if;
		end if;
	end process;
 

just ditch the idea of slower "clocks" altogether. You should ideally have a single system clock, and that is it. With SPI, the SCK is usually much slower than your system clock, so you can treat it as any other signal, and synchronise it to your system clock and do edge detection. Use this edge detection as a clock enable to your incoming data (also synchronised) registers.

For outgoing data, again, just create clock enables. You have problems in your current code because you see to have the same idea of generating clocks (although CLK10 and CLK5 are always '1' and dont toggle), but they are generated from a clock enable. You should ditch these clock altogether, and generate clock enables:


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
signal cnt          : unsigned(3 downto 0)  := (others => '0');
signal en_1_in_16   : std_logic;
signal en_1_in_2    : std_logic;
signal en_1_in_8    : std_logic;
 
process(clk)
begin
  if rising_edge(clk) then   
    cnt         <= cnt + 1;
    
    -- Defaut enables to 0
    en_1_in_16  <= '0';
    en_1_in_2   <= '0';
    en_1_in_8   <= '0';
    
    if cnt = 0 then
      en_1_in_16  <= '1';
    end if;
    
    if cnt rem 2 = 0 then
      en_1_in_2   <= '1';
    end if;
    
    if cnt rem 8 = 0 then
      en_1_in_8   <= '1';
    end if;
  end if;
end process;

 
I have simulated the code you posted here and what I see are 3 signals:
-Clock @50MHz
-Clock2 @ 3.125MHz
-Clock3 @ 6.25MHz

Also I observe the duty cycle is not 50%. So I have some doubts maybe you can solve.
Is a very elegant way of clock generation, but, can I use the same values I posted on my OP? Can i get a 50% duty-cycle?

The main idea is to make a SPI protocol, so i must work with phase and polarity, will I be able to do it going that way?
 

I think the main challenge is the need to run the core at 50MHz with a 50MHz system clock and also lower rates.

The general way to deal with this is some form of SIPO/PISO interface and/or frequency synthesis to avoid needing to have a process with a falling edge.
 

I have created the clocks I wanted, but I still worried about the duty-cycle. As far as I know, I must use a 50% duty-cycle for SPI, no? Or at least bwtween 40-60%?

Relojes.PNG
 

The signals shown in your simulation are clock enable signals, used to enable register transfers in the 50 MHz clock domain. They won't be used as SPI clocks and don't need symmetrical duty cycle. By design, the are always one system clock period wide.

Instead consider a clock enable signal with double the intended SCK frequency, it can be used to toggle SCK in the active transmission phase, also set nCS and SDO and sample SDI in combination with the actual SCK phase.
 

It seems that I still not understanding the "clock enable" concept you all talk me about. From the code TrickyDicky posted I have made the following one, which toggles and creates clock signals.
Code:
process(CLK_50)
begin
	if rising_edge(CLK_50) then   
		cnt	<= cnt + 1;
		--25MHz
		if cnt rem 1 = 0 then
			CLK_25   <= NOT(CLK_25);
		end if;
		--5MHz
		if cnt rem 5 = 0 then
			CLK_5   <= NOT(CLK_5);
		end if;
		--1MHz
		if cnt rem 25 = 0 then
			CLK_1   <= NOT(CLK_1);
		end if;
		--Reset
		if(cnt>49)then
			cnt<="000001";
		end if;
	end if;
end process;

Is it OK to use the generated clock signals as SCLK, or am I missing something?
 

When the division is not a multiple of 2, the duty cycle can be 50-50 with the following method; unless you have some sort of setup and hold time constraint the pulsed clock should be enough.


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
architecture rtl of clk_divn is
 
    signal pos_count, neg_count : unsigned(count_size-1 downto 0);
 
begin  
    pos_cnt_proc : process(clk_in) is
    begin
        if rising_edge(clk_in) then
            if rst='1' then
                pos_count <= (others => '0');
            else
                if pos_count = div-1 then 
                    pos_count <= (others => '0');
                else 
                    pos_count <= pos_count + 1;
                end if;
            end if;
        end if;
    end process;
    neg_cnt_proc : process(clk_in) is
    begin
        if falling_edge(clk_in) then
            if rst='1' then
                neg_count <= (others => '0');
            else
                if neg_count = div-1 then 
                    neg_count <= (others => '0');
                else 
                    neg_count <= neg_count + 1;
                end if;
            end if;
        end if;
    end process;
 
    clk_out <= '1' when (pos_count > (div/2)) OR (neg_count > (div/2)) else '0';
end architecture rtl;

 

Hi,

your HDL skills are improving ;-)

My question: Do you need all 3 clocks at the same time? Or do you need just one clock?

Your approch seems to be OK. You generate "signals" but they are not "clock signals" in the meaning of FPGA system clocks (with their own clock distribution network).
And yes you can use those (non clock) signals as SPI SCK.

But don´t use this signals internally as clock for new signals like MOSI, MISO... [--> NOT: "if rising_edge(SCK) ..]
*****

I recommend to generate an SPI_Ena signal with twice the SCK frequency.

always run a counter 0...n
n= (50MHz / 2 / SCK_freq) -1
n=0 for 25MHz
n=4 for 5MHz
n=24 for 1MHz


Here some pseudo code:
Code:
process(CLK_50)
begin
   if rising_edge(CLK_50) then   
      if cnt  >= n then
         SPI_Ena = 1 
         cnt = 0
      else
         SPI_Ena = 0
         cnt	<= cnt + 1;
      endif
   end if;
end process;
     
              
process(SPI)
begin
   if rising_edge(CLK_50) then   (this is the system clock)
      if SPI_ENA then                   (and this is the ENABLE for processing SPI signals)
         SCK =  <= NOT(SCK);

      ...here do the other SPI processes... (FSM to write MOSI, read MISO, count the bits, maybe control !CS...)

      end if;
   end if;
end process;

There may be other - better - solutions.
AS already said: I´m not familiar with writing HDL, there may be better experts


Klaus
 
Generation of clk_out in combinational logic involves the problem of possible glitches, thus it shouldn't be used as external signal, e.g. drive the SPI clock.

A simple example how things can be done.

Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
PROCESS(clk,reset)
BEGIN
  IF reset = '1' THEN
    bitcnt <= 0;
    nCS_DAC <= '1';
    DACCLK <= '0';
  ELSIF rising_edge(clk) THEN
    IF clkcnt > 0 THEN
      clkcnt <= clkcnt - 1;
    END IF;   
    CASE bitcnt IS
      WHEN 0 =>
        DACCLK <= '0';        
        IF start = '1' AND clkcnt = 0 THEN
          sr <= data;
          nCS_DAC <= '0';
          bitcnt <= 8;
          clkcnt <= 15;
        END IF;
      WHEN OTHERS =>
-- SPI frequency = input frequency/16, 50% duty cycle                
        IF clkcnt = 8 THEN
          DACCLK <= '1';
        ELSIF clkcnt = 0 THEN    
          clkcnt <= 15;
          sr <= sr(6 downto 0) & "0";
          DACCLK <= '0';
          bitcnt <= bitcnt - 1;
          IF bitcnt = 1 THEN
            nCS_DAC <= '1';
          END IF;
        END IF;
      END CASE;      
  END IF;
END PROCESS;
MOSI <= sr(7);

 

Ok, now I got the idea clearer.
So Klaus, what you were telling to me on the I2C thread is what we are discussing on this one, isn't it?

If that's the point I was missing, then I probably rewrite the code o fix it up.

About the clocks on SPI, I just need one clock at a time. The idea of multiple clock speeds is just to make a generic module compatible with multiple devices, from temperature sensors to small screens.
 

It's quite easy to switch a clock enable divider between different rates, the decision between SCK edges would be made a bit different than in my above example.

Please review my comment about variable SPI speed in post #2, you should check if it's useable with your devices.
 

Hi,

So Klaus, what you were telling to me on the I2C thread is what we are discussing on this one, isn't it?

It´s a problem on how to treat clocks in a CPLD or FPGA, thus it´s a general problem writing correct HDL .... it´s no problem of generating I2C or SPI signals.

Although possible I don´t recommend to generate different SCK (frequencies) at the same time. But you are free to generate several SCK_ENA at the same time .. and just select the one ENA signal you like.


Klaus
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top