Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
17241 Discussions

Building an SPI Decoder into Altera

Altera_Forum
Honored Contributor II
2,019 Views

Hello, 

 

I am working on a project which requires interfacing between a Microcontroller and an Altera FPGA. I'm using an Arduino UNO and DE0Nano Cyclone IV. The Arduino sends a 64bit word via SPI into the Altera. This word has been verified using a Logic Analyzer, and it seems to be stable and correct. In order to read the 64 bits, I am loading everything into a shift register. This procedure works as well since my outputs are correct. The first process in the code snippet below reads the data through SPI, making sure the Selector is active low and reading on the RE of the clock. The second process decodes the 64 bit word into the specific outputs I need. It does this at the end of a data transmission, when the Selector is going back to the idle state High. 

 

The issue I'm running into is when the circuit is idle, when SPI is not running in the Microcontroller. From the code, since SPI_Sel is High and SPI_Clk is not operating, I expect the shift register to be set to 0 and no value should change. However, this is not the case and I get a flickering in my values. If I remove the line where I load into the shift_reg64(63 downto 0), this flickering stops and obviously nothing happens. Since, the flickering stops, I believe that data is being loaded and read from the shift register without it being ready. Does anyone have any idea of what may be causing this? 

 

Thank you very much! Below is the code I'm referencing. 

 

process (SPI_Clk, SPI_Data, SPI_Sel, shift_reg64) begin if rising_edge (SPI_Clk) then if (SPI_Sel = '0') then --shift data in and load new shift_reg64(63 downto 0) <= shift_reg64(62 downto 0) & SPI_Data; end if; end if; end process; process (SPI_Sel, shift_reg64) begin if rising_edge(SPI_Sel) then outA <= "000" & shift_reg64(60 downto 32); outB <= "000" & shift_reg64(28 downto 0); SEL_h <= shift_reg64(63 downto 61); SEL_l <= shift_reg64(31 downto 29);- end if; end process;
0 Kudos
13 Replies
Altera_Forum
Honored Contributor II
966 Views

Moving to the VHDL coding subforum. Unfortunately my VHDL is too rusty for me to offer a suggestion on what might be happening. 

 

Some observations/suggestions: 

 

- I recommend implementing reset conditions for your registers 

- If your data that is being transferred from one clock domain to the SPI clock domain you should probably refactor your code to operate on the data clock domain (which I assume is faster) and use the SPI clock as a shift enable
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

normally the process list should only contains the clock (or an asynchrounous reset) so you could try by changing 

process (SPI_Clk, SPI_Data, SPI_Sel, shift_reg64) begin 

into 

process (SPI_Clk) 

 

and 

 

process (SPI_Sel, shift_reg64) 

into 

process (SPI_Sel) 

 

but I still wouldn't recommend this because you create two clock domains by doing this (SPI_SEL & SPI_Clk). I think it's much better if you process everything on some clock present in your FPGA (maybe the clock you use to read the outA , outB, ... with ? )
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

For SPI, its much better to use a fast system clock and resample all of the SPI signals. This way you're using a single clock domain.

0 Kudos
Altera_Forum
Honored Contributor II
966 Views

Hi, 

 

BadOmen: Thanks for your suggestions and for moving the thread to a correct subforum. Sorry for posting into the wrong one. 

 

k0007 and Tricky: I understand what you're saying, but I have a few questions! Thanks for your help. First, isn't the SPI clock synced with the serial data in order to have a stable output at every clock sample? If I use my internal FPGA clock, which is about 20x faster, I believe, wouldn't I be oversampling and get corrupted data. Or what do you mean by resampling? Store the SPI info on a tmp register and then load it into a new register which is loaded at every FPGA clock cycle? Also, by creating two clock domains you mean the fact that I'm using two if-else statements? But the SPI_Sel acts just as an enable, so I don't see how this could cause a problem. Finally, regarding the sensitivity list,the compiler throws warnings if other values I'm reading (ie _Data, _Sel, shift_reg64) are not in the sensitivity list. I'm not 100% sure what it represents exactly... If you could clarify this, I'd very much appreciate it! 

 

Thanks for your help, and I'm sorry I rambled a bit there. 

 

Gabriel
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

The sensitivity lists are fairly meaningless for synthesis - they are ignored. The logic is taken just from whats inside the process. 

Using the system clock is just fine. If you read the SPI spec you'll see it has some leeway on timing. It is much better to use the single system clock and treat the SPI_clk as just another signal. Detect the rising edge of it and then you know where the data is.
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

From what I take, you're suggesting something like this? 

 

process (Clk, Reset, SPI_Clk) begin if Reset = '1' then shift_reg64 <= x"0000000000000000"; elsif rising_edge(Clk) then if rising_edge(SPI_Clk) then shift_reg64(63 downto 0) <= shift_reg64(62 downto 0) & SPI_Data; end if; end if; end process;  

 

But this code doesn't compile... Any idea?
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

Actually, just tried this, since I thought it made more sense. It now compiles but it doesn't work. It's not reading the data correctly, but I believe I'm getting closer to what you mean. 

Here's the code: 

 

process (SPI_Clk) begin if rising_edge(SPI_Clk) then if SPI_Sel = '0' then dff <= SPI_Data; end if; end if; end process; process (Clk, Reset, SPI_Clk) begin if Reset = '1' then shift_reg64 <= x"0000000000000000"; elsif rising_edge(Clk) then shift_reg64(63 downto 0) <= shift_reg64(62 downto 0) & dff; end if; end process; process (SPI_Sel) begin if rising_edge(SPI_Sel) then outA <= "000" & shift_reg64(60 downto 32); outB <= "000" & shift_reg64(28 downto 0); SEL_h <= shift_reg64(63 downto 61);--"000"; SEL_l <= shift_reg64(31 downto 29);--"000"; end if; end process;
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

Ok, this is my newest status. I built a state machine to make the SPI_Clk into a regular signal and this is what I came up with: 

 

process (Clk, Reset) begin if Reset = '1' then shift_reg64 <= x"0000000000000000"; elsif rising_edge(Clk) and SPI_Clk = '1' then shift_reg64(63 downto 0) <= shift_reg64(62 downto 0) & SPI_Data; end if; end process; process (SPI_Sel) begin if rising_edge(SPI_Sel) then outA <= "000" & shift_reg64(60 downto 32); outB <= "000" & shift_reg64(28 downto 0); SEL_h <= shift_reg64(63 downto 61);--"000"; SEL_l <= shift_reg64(31 downto 29);--"000"; end if; end process;  

 

This looks pretty much the same, but in my top-level .bdf I'm running the SPI clock straight from GPIO through two D-FFs in series which are clocked with the 50MHz clock. The output of the second D-FF goes into the SPI decoder from the code above as SPI_Clk. I believe now this should be working right, but somehow it still isn't!! If anyone can see where I went wrong, I'd deeply appreciate it! Thanks!!
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

You're getting close; 

 

1. Add logic to detect the rising-edge of SPI clock 

 

Hint: you can create a single clk period pulse by routing the synchronized spi_clk output through a register, and then using a gate, eg., or, and, xor, with the synchronized and delayed synchronized signal. Call this spi_clk_rising ('cause later you may need an spi_clk_falling version :) ) 

 

2. Use the rising-edge pulse as an enable to your shift-register, i.e., in the code above, use 

 

process (Clk, Reset) begin if Reset = '1' then shift_reg64 <= (others => '0'); elsif rising_edge(Clk) then if (spi_rising_edge = '1') then shift_reg64(63 downto 0) <= shift_reg64(62 downto 0) & SPI_Data; end if; end if; end process;  

 

Cheers, 

Dave
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

Hi Dave, thanks for your help!! 

 

I added edge detection logic, routing the SPI_Clk signal into a D-FF and using Q' ANDed with SPI_Clk as my spi_rising_edge. I believe this logic works well. Then, I loaded data into shift_reg64 as you suggested, using spi_rising_edge as an enable. So all this should work fine, right? I think I'm still losing some info on my data though. 

 

Could it be the way I'm reading from the register to define the outputs? 

 

process (SPI_Sel) begin if rising_edge(SPI_Sel) then outA <= "000" & shift_reg64(60 downto 32); outB <= "000" & shift_reg64(28 downto 0); SEL_h <= shift_reg64(63 downto 61);--"000"; SEL_l <= shift_reg64(31 downto 29);--"000"; end if; end process;  

 

Is there an error in this or does it look fine? 

 

Thanks! 

 

Gabriel
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

You're using an asynchronous signal as a clock, bad idea. 

 

If you want to use SPI select deasserting to indicate when to load a parallel output register, then you can use exactly the same edge-detection technique to generate a pulse when SPI_sel deasserts. You need to have a dual-DFF synchronizer, followed by your edge-detect logic. 

 

Change the process to use your FPGA clock, and use the spi_sel_rising_edge pulse to enable your parallel output registers at the end of each SPI transaction. 

 

Cheers, 

Dave
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

Great! I believe this worked! Just to make sure though, I still have a doubt on using the dual DFF synchronizer and the edge-detection logic. 

Take I have my input SPI_Clk going into DFF1 and Q1 going into DFF2. Q2' is NOTed and ANDed with Q1. This give the SPI_rising_edge. 

 

I really appreciate your help!
0 Kudos
Altera_Forum
Honored Contributor II
966 Views

 

--- Quote Start ---  

Just to make sure though, I still have a doubt on using the dual DFF synchronizer and the edge-detection logic. 

Take I have my input SPI_Clk going into DFF1 and Q1 going into DFF2. Q2' is NOTed and ANDed with Q1. This give the SPI_rising_edge. 

 

--- Quote End ---  

 

Post your code (upload it as an attachment, don't post it inline). I'll take a look at it. Bonus points if you can create a Modelsim simulation :) 

 

Cheers, 

Dave
0 Kudos
Reply