Jump to content
  • 0

SPI - Arduino to Basys 3


Tickstart

Question

Arduino is the SPI Master and therefore provides the clock, SPICLK through a PMOD. How do I receive the clock in a good way on the FPGA?

Vivado does not approve of checking rising_edge(SPICLK) so I though I'd put a clock buffer or something in between (not that I know why or what they do but it sounds like a good idea). At some point Vivado told me to add "set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets {SPICLK_IBUF}]" to the constraints file, but I still got warnings and it didn't recommend I proceed.

 

If I have the top level SPICLK connected to an IBUF_IBUFDISABLE with the disable line connected to the slave select (SS) line, I get this warning:

[DRC 23-20] Rule violation (CKLD-2) Clock Net has IO Driver, not a Clock Buf, and/or non-Clock loads - Clock net spi_buf is directly driven by an IO rather than a Clock Buffer or may be an IO driving a mix of Clock Buffer and non-Clock loads. This connectivity should be reviewed and corrected as appropriate. Driver(s): IBUF_IBUFDISABLE_inst/O
 

If I have the top level SPICLK connected to an IBUF_IBUFDISABLE and that into a BUFGCE, with the disable line connected to the slave select (SS) line and the inverse of SS into the CE, I get this warning:

[Place 30-574] Poor placement for routing between an IO pin and BUFG. This is normally an ERROR but the CLOCK_DEDICATED_ROUTE constraint is set to FALSE allowing your design to continue. The use of this override is highly discouraged as it may lead to very poor timing results. It is recommended that this error condition be corrected in the design.

    IBUF_IBUFDISABLE_inst (IBUF_IBUFDISABLE.O) is locked to IOB_X0Y25
     and BUFGCE_inst (BUFGCTRL.I0) is provisionally placed by clockplacer on BUFGCTRL_X0Y1

Roughly the same warning was issued with just the BUFGCE.

 

I know there are other ways of polling the input clock from the arduino and treating it as normal signal but I want to do it the "proper" way.

Link to comment
Share on other sites

Recommended Posts

1 hour ago, D@n said:

LED[1] should be on E19, not 19.  If that's not it then can you post a screen shot of the error and a copy of your XDC file?

Dan

## LEDs
set_property PACKAGE_PIN U16 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
#set_property PACKAGE_PIN E19 [get_ports {led[1]}]
#set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property PACKAGE_PIN U19 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
ETC

I can't unprohibit the pin either.. I guess that led is disabled forever. Bullshit

Link to comment
Share on other sites

Well ... perhaps.  I have come across problems that needed rebuilding the project from the source files.  In my case, that was always because I had some generated products from a prior version of Vivado lying around.  The error I was getting didn't make sense then either, and asking on the xilinx forum didn't help, but rebuilding the project did ... so, it may help.

Dan

Link to comment
Share on other sites

35 minutes ago, D@n said:

Well ... perhaps.  I have come across problems that needed rebuilding the project from the source files.  In my case, that was always because I had some generated products from a prior version of Vivado lying around.  The error I was getting didn't make sense then either, and asking on the xilinx forum didn't help, but rebuilding the project did ... so, it may help.

Dan

Is there any way to "clean" a project or something? I created a new one and JUST tried using led[1], still wasn't allowed..

Link to comment
Share on other sites

@D@n,

Tell you what mate :D I had swapped the yellow cable on my breadboard for the orange one. Once I swapped them over it worked! At least some past of the communication. I send a byte over the MOSI-line and link each bit up to leds on the Basys 3. Each second I increment the data sent (0 - 255) and a pretty binary pattern shows up! =)

Link to comment
Share on other sites

I found this; https://www.xilinx.com/support/answers/64736.html

I disabled "set_property BITSTREAM.CONFIG.PERSIST YES [current_design]" in my .XDC-file and the problem seemed to go away. Very stange indeed.

 

I'll see if it did indeed work and especially if it worked in the project I actually care about..

 

EDIT: Yep, it works again! from UG899:

"° Enable Prohibit usage of the configuration pins as user I/O and persist after
configuration to ensure that pins are used as configuration pins and not as general
purpose I/Os after configuration. When you select this option, the following
constraint is created when you save the design:
set_property BITSTREAM.CONFIG.PERSIST YES [current_design]
Note: For more information on the configuration bitstream settings, see this link in the
Vivado Design Suite User Guide: Programming and Debugging (UG908) [Ref 11]."

Link to comment
Share on other sites

Great discussion.

I am working on SPI.. I want to make STM32 controller works at 72 Mhz as SPI Master and Basys 3 as SPI Slave that has frequecy of 100 Mhz.

I have read somewhere that SPI Master frequecy <= 2 (SPISlave frequecy). So, I have to add any clock divider in BASYS 3 VHDL code or it will work fine without that as it fulfills the condition.

Link to comment
Share on other sites

Hi again, thanks for your replies. I had thoroughly messed up with the code for the synchronizer which I now have taken care of. Here's a simulation of the new and improved code to delay the SCL-signal from the Arduino two cycles to make it stable. I also made a signal that signifies the rising edge of the clock so I can run it on the fpga without considering the difference in speed between the two.

 

 

synchro_edit.PNG

Link to comment
Share on other sites

@Tickstart,

"Polling the input clock and treating it as a normal signal" is almost exactly what I would do.

There would be at least one difference, though, between that and what I would recommend: You need to take the inputs from the SPI port and apply a circuit to synchronize those inputs to your FPGA system clock.  To do that, register the inputs twice before using them.  So, something like:

reg	[1:0] clk_transfer;
always @(posedge i_clk)
	clk_transfer <= { clk_transfer[0], i_spi_sck };
   
wire	ck_sck; // Our version of the SCK signal, having gone through the clock transfer
assign	ck_sck = clk_transfer[1];

reg	ck_stb;	// True on any positive edge of the clock
always @(posedge i_clk)
	ck_stb <= (clk_tranfer[2:1] == 2'b01);

Something like the above code, that takes two clocks to synchronize the incoming SPI, should always take place whenever an input comes into your design that has not been synchronized to your own clock.  If your are curious as to why, look up metastability.  If you don't do this, you may find that the clock signal you use .. isn't reliable across your FPGA.  (For some logic, it'll act like it was true, for other logic it'll act like it was false, etc.)

With the above code, you can then gate your logic to only run anytime (ck_stb) is high (will only happen for one clock interval per i_spi_sck tick) and (i_spi_cs_n) is low. 

If you know the other lines are stable whenever the clock ticks, you might get away without synchronizing them as well.

But ... that's basically how you do it.

Dan

Link to comment
Share on other sites

1 minute ago, D@n said:

@Tickstart,

"Polling the input clock and treating it as a normal signal" is almost exactly what I would do.

There would be at least one difference, though, between that and what I would recommend: You need to take the inputs from the SPI port and apply a circuit to synchronize those inputs to your FPGA system clock.  To do that, register the inputs twice before using them.  So, something like:


reg	[1:0] clk_transfer;
always @(posedge i_clk)
	clk_transfer <= { clk_transfer[0], i_spi_sck };
   
wire	ck_sck; // Our version of the SCK signal, having gone through the clock transfer
assign	ck_sck = clk_transfer[1];

reg	ck_stb;	// True on any positive edge of the clock
always @(posedge i_clk)
	ck_stb <= (clk_tranfer[2:1] == 2'b01);

Something like the above code, that takes two clocks to synchronize the incoming SPI, should always take place whenever an input comes into your design that has not been synchronized to your own clock.  If your are curious as to why, look up metastability.  If you don't do this, you may find that the clock signal you use .. isn't reliable across your FPGA.  (For some logic, it'll act like it was true, for other logic it'll act like it was false, etc.)

With the above code, you can then gate your logic to only run anytime (ck_stb) is high (will only happen for one clock interval per i_spi_sck tick) and (i_spi_cs_n) is low. 

If you know the other lines are stable whenever the clock ticks, you might get away without synchronizing them as well.

But ... that's basically how you do it.

Dan

So basically a debouncer for the incoming SPI master clock?

Link to comment
Share on other sites

@Tickstart,

It's not a debouncer, but a synchronizer, even though it may have much in common with a debouncer.  Unlike a debouncer, you want the synchronization to take place as fast as possible.  Hence ... only two clocks.  Some folks suggest three, I've never heard anyone suggest more than three--it's all a matter of the reliability you wish to create.

Debouncers often have programmable wait intervals ... synchronizers have only the two clocks.

Dan

Link to comment
Share on other sites

8 minutes ago, D@n said:

@Tickstart,

It's not a debouncer, but a synchronizer, even though it may have much in common with a debouncer.  Unlike a debouncer, you want the synchronization to take place as fast as possible.  Hence ... only two clocks.  Some folks suggest three, I've never heard anyone suggest more than three--it's all a matter of the reliability you wish to create.

Debouncers often have programmable wait intervals ... synchronizers have only the two clocks.

Dan

Do I need to synchronize on the falling edge of the incoming SCLK too (let's say I only care about rising edge)?

Link to comment
Share on other sites

@Tickstart,

In the logic I presented above, the clock was synchronized before I placed the rising edge detection into clk_stb.  If you cared about the falling edge, you could create a ck_falling signal anytime the incoming clk_transfer pipe had 2'b10 in the upper two bits.  Do you *need* to if you don't care about the falling edge?  Not at all.

Oh, one more thing, only transition on your system clock.  You don't want to do any "always @(posedge ck_stb)" stuffs, neither do you want to do any "always @(posedge ck_clock)" or falling edge for that matter.  Keep everything on the same system clock, "always @(posedge i_clk), as I called it in my example.

Dan

Link to comment
Share on other sites

Alright, please give me some input on my super simplistic SPI slave code for the FPGA. It doesn't work, but I don't know if that's due to the Arduino or the Basys 3:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity spi_receiver is
    Port ( clk, SPICLK, MOSI, SS : in STD_LOGIC;
           sw : in STD_LOGIC_VECTOR ( 0 downto 0 );
           MISO : out STD_LOGIC;
           led : out STD_LOGIC_VECTOR ( 15 downto 8 ));
end spi_receiver;

architecture Behavioral of spi_receiver is

component IO_sync is
    Port ( clk, io_in : in STD_LOGIC;
           io_out, io_tick : out STD_LOGIC);
end component;

signal spi_clk, spi_clk_tick, slave_select, mosi_sync : STD_LOGIC;
signal data_reg, led_data : STD_LOGIC_VECTOR ( 7 downto 0 ) := X"00";

begin

    SPICLK_SYNC: IO_sync port map(clk => clk, io_in => SPICLK, io_out => open, io_tick => spi_clk_tick);
    SS_SYNC: IO_sync port map(clk => clk, io_in => SS, io_out => slave_select);
    MISO_SYNC: IO_sync port map(clk => clk, io_in => MOSI, io_out => mosi_sync);

    spi_clk <= '0' when slave_select = '1' else spi_clk_tick;
    
    TRANSCIEVE: process(clk)
    begin
        if rising_edge(clk) and (not slave_select and spi_clk) = '1' then
            data_reg <= data_reg(6 downto 0) & mosi_sync;
        end if;
    end process;

    led_data <= data_reg when slave_select = '1' else led_data;

    led <= led_data;

    MISO <= sw(0); -- just whatever right now

end Behavioral;

The IO_sync is as such, I simulated it so it works as intended. After two cycles the io_in is output to io_out, and io_tick is true on the "rising edge" of it:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity IO_sync is
    Port ( clk, io_in : in STD_LOGIC;
           io_out, io_tick : out STD_LOGIC);
end IO_sync;

architecture Behavioral of IO_sync is

component D_flip_flop_rst is
    Port ( clk, d, rst : in STD_LOGIC;
           q : out STD_LOGIC);
end component;

component D_flipflop is
    Port ( clk, d : in STD_LOGIC;
           q : out STD_LOGIC);
end component;

component T_vippa is
    Port ( clk, t, rst : in STD_LOGIC;
           q : out STD_LOGIC);
end component;

signal intermediate : STD_LOGIC;

begin

    DFF0: D_flipflop port map(clk => clk, d => io_in, q => intermediate);
    DFF1: D_flip_flop_rst port map(clk => clk, d => intermediate, rst => io_in, q => io_out);
    TICK: T_vippa port map(clk => clk, t => intermediate, rst => io_in, q => io_tick);

end Behavioral;

EDIT* Aha, yep I found at least one problem. Since the SS is active low, my desynchronization module won't work properly. I need to invert it somehow. I will set about doing that.

EDIT#2: Still does not work after fixing the former error.

EDIT#3: The code for the synchro is garbage, disregard it. Read later replies for rectifications.

Link to comment
Share on other sites

@Tickstart,

I've looked over your code several times and haven't seen anything.  That "if", though, on (the positive edge of the clock and other stuff) bothers me, but I'm not enough of a VHDL guru to say if it looks good or not.  Sorry.  Have you simulated your code?  That's what I'd be doing now if I got that far and things weren't working.

Next, if it works in simulation, can you tell what is failing and where within your code?  I'm trying to slowly put together a list of things that can be done when you get stuck to get you unstuck, but ... my examples are in Verilog (not VHDL) and they are far from complete.

Dan

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...