# Can't provide an input clock signal to an SPI slave implemented in an FPGA through a PMOD pin

I have implemented a dummy SPI slave device within an FPGA (Basys 3). The master device is in an MCU.

I'm trying to connect the clock signal generated by the master (MCU) to the slave clock pin (a PMOD pin in the FPGA). However, it seems that Vivado doesn't allow to provide clock signal as an input, and it stops in the implementation step. Unless I comment out this PMOD assignment, in which case it can finish the impl. step.

The error I'm getting is:

[Place 30-574] Poor placement for routing between an IO pin and BUFG. If this sub optimal condition is acceptable for this design, you may use the CLOCK_DEDICATED_ROUTE constraint in the .xdc file to demote this message to a WARNING. However, the use of this override is highly discouraged. These examples can be used directly in the .xdc file to override this clock rule.
< set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets JA_IBUF[3]] >

JA_IBUF[3]_inst (IBUF.O) is locked to IOB_X1Y97
and JA_IBUF_BUFG[3]_inst (BUFG.I) is provisionally placed by clockplacer on BUFGCTRL_X0Y31


and it's caused because I'm using JA[3] (Pmod pin) as an input clock signal for the SPI slave device.

I know there is a number of ways of fixing this, like generating a fast clock signal within the FPGA and pass the generated MCU clock signal through an edge detector, but do I really need to do this? (I have tried this but breaks my design)

I have read that there should be some pins that are valid for routing clock signals, however I haven't found which ones are in Basys 3.

Is there any way to provide a clock signal as an input to an FPGA?

I have tried to do what the error message says, that is, adding:

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets JA_IBUF[3]]


to the constraints file. This works, but the error message 'highly discourages' doing it. Is there any 'good' way of doing this?

• What Vivado considers is a clock signal is different to your spi clock. It is simply an input that your logic interprets as the spi clock. Nov 17, 2021 at 22:55
• I wonder if you could screenshot the exact error.... Nov 18, 2021 at 13:17
• How have you implemented your slave SPI inside the FPGA? Is it fully custom (your code), or using the Xilinx provided IP? Using the Xilinx IP can have an impact on the signal types, so this is important. Nov 18, 2021 at 17:43
• Does Vivado actually crash or just stop with an error? Nov 18, 2021 at 18:40
• It stops with an error. Nov 18, 2021 at 18:41

Depending on the speed of your SPI clock, a typical solution to this is to use oversampling and filtering of the incoming SPI clock signal using a higher speed clock already available in the FPGA (e.g. fed in from dedicated clock pin).

The idea is that rather than using the SPI clock as a clock for your SPI shift register logic, you instead use a high speed clock (e.g. 50MHz) to drive everything. The SPI clock signal is fed first through a multi-flop synchroniser (e.g. 2 DFFs driven by the sample clock), then goes into an edge detector to see when it transitions from low-to-high or high-to-low. The edge detector output is then used as a clock enable for the shift register. You can also add a filter as part of the edge detection - e.g. only detect changes after three consecutive samples:

// Synchroniser (do the same for MOSI and SS)
reg [1:0] sclk_sync;
always @ (posedge clock50) begin
sclk_sync <= {sclk_sync[0], sclk};
end
// Optional Filter - avoids glitches caused by noise/etc.
reg [2:0] sclk_dly;
reg sclk_filt;
always @ (posede clock50 or posedge reset) begin
if (reset) begin
sclk_dly <= 3'b0;
sclk_filt <= 1'b0;
end else begin
sclk_dly <= {sclk_dly[1:0],sclk_sync[1]};
if (&sclk_dly) begin
sclk_filt <= 1'b1; //Set high if sclk high for 3 clock cycles
end else if (~|sclk_dly) begin
sclk_filt <= 1'b0; //Set low if sclk low for 3 clock cycles
end
end
end
// Edge detect
reg sclk_old;
wire sclk_rise;
wire sclk_fall;
assign sclk_rise = (!sclk_old & sclk_filt); // Rising edge if was low and is now high
assign sclk_fall = (sclk_old & !sclk_filt); // Falling edge if was high and now low
always @ (posedge clock50 or posedge reset) begin
if (reset) begin
sclk_old <= 1'b0;
end else begin
sclk_old <= sclk_filt;
end
end

// MOSI/MISO logic
always @ (posedge clock50 or posedge reset) begin
if (reset) begin
miso <= 1'b1;
end else begin
// Clock out MISO on falling edge?
if (sclk_fall) begin
miso <= shiftReg[end];
end
// Clock in MOSI on rising edge?
if (sclk_rise) begin
shiftReg <= {shiftReg[end:1], mosi_sync[1]};
end
end
end


By doing this your SPI clock is no longer treated as a clock by the FPGA tools - it can instead be treated as a data line. In addition it means the rest of the logic the SPI slave connects to (e.g. memories, registers, status flags, etc.) can run on the existing high speed clock without the need for any clock domain crossing. It also means you can have a state machine that can act on both rising and falling edge of the SPI clock (e.g. read value of MOSI on rising edge, update MISO on falling edge) without needing multi-edge sensitivity (which most FPGAs can't do).

• I tried something similar before but had problems: I tried with a 50MHz clock to drive everything and connect the SPI clock to a edge detector, and then do, on the pos. edge of the 50MHz, if (spi_clk_edge_det_out) ... The problem is that the data signal (mosi) gets a bit delayed and ends up giving the wrong result Nov 18, 2021 at 22:15
• @Martel then you must be doing something wrong in your SPI logic, like maybe your FPGA system clock is not 'fast enough' compared to SPI clock, or maybe you are sampling in the wrong edge, for eg: sampling on rising edge instead of falling edge, this depends on SPI mode and how the data and clock are aligned from the SPI master side. Nov 19, 2021 at 10:08
• @MituRaj Yes, I was using the pos. edge detector for the SPI TX (negative edge). That was causing the failure, but now it is fixed. However, instead of 'oversampling', that is, checking the SPI clk signal on the pos. edge of the (much faster) FPGA clk. signal, I have directly done 'always @ (negedge pulse) begin .... ' being 'pulse' the output of the neg. edge detector. Is there any drawback in doing this? Nov 19, 2021 at 16:26
• @Martel using the output of the edge detector as a clock is not ideal. Effectively you are turning the edge detector into a new clock domain which is no longer truly synchronous to your fast clock (because it will be delayed by the logic that created it), which requires a great deal of care to ensure that issues such as metastability and concurrency don't arise. Given your edge detector output is generated in the fast clock domain, it is far simpler and safer to use the example shown above - using sensitivity to the fast clock, and then if (risingEdge) Nov 19, 2021 at 17:05

If you are bringing an external clock into the FPGA, you should be mapping this IO signal to Clock Capable IO pins (CCIO) on your FPGA. This makes sure the clock will have access to different clock regions and enables global clock tree routing through buffers. Currently, I guess you are using ordinary IO pins to map clock, hence the DRC errors/warnings. Which you are bypassing with some custom command at the cost of signal integrity.

As far as I understood from skimming through, Basys-3 uses Artix-7 series FPGA and it has four CCIO pins per IO bank.

Refer Artix-7 FPGA Packaging and Pinout Doc: http://padley.rice.edu/cms/OH_GE21/UG475_7Series_Pkg_Pinout.pdf

You may have to dig in the above document for your FPGA part no. to find the exact pins for the banks you are looking for.