- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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;
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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 ? )- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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;
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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!!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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!- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
--- 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

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page