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.

A simple SPI based Parallel In Serial Out shift register

Status
Not open for further replies.

Endymion

Junior Member level 3
Joined
Nov 18, 2011
Messages
29
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,281
Activity points
1,611
I'm writing a simple SPI based shift register in VHDL for a Max V CPLD. I've come up with the following code:

Code:
library ieee;
use ieee.std_logic_1164.all;

entity PISO is
  port(CLK, SI, nCS : in std_logic;
					outpin: out std_logic;
					PI  : in  std_logic_vector(7 downto 0);
					SO  : out std_logic);
end PISO;


architecture archi of PISO is
  signal tmp: std_logic_vector(PI'high downto PI'low);
  signal bitOut: std_logic;
  begin 
    process (CLK,nCS)
      begin
        if (nCS='1') then
			     tmp <= "10101111";
        elsif falling_edge(CLK) then
            tmp <= tmp(PI'high -1 downto PI'low) & '0';
			   
        end if;
    end process;
	 SO <= tmp(PI'high) when nCS = '0' else 'Z';
	 outpin <= '1';
end archi;

Note the line "tmp <= 10101111" is just for testing. In reality, I will load PI into tmp. I've tested the code and it works well when I utilize Mode 0 SPI (it doesn't work when CPOL = '1' and I change elsif falling_edge(CLK) to elsif rising_edge(CLK)). Would appreciate some advice on this.

But is the above code improvable? Am I falling into some newbie pitfalls of VHDL?
 

First, this isn't really SPI,
Only a very small portion of the protocol.
By definition, at the heart of an SPI peripherial is a "rotating register".
It should samle the input at the same time as it drives the output.

Second,
why do you use falling edge registers?

third,
nCS, is used as a reset.
I wouldn't do that. a reset signal has a very important role in a synchronous design and it's global (same as clock).
I'll rewrite it:

process (CLK,RST)
begin
if (RST='1') then
tmp <= "10101111";
elsif rising_edge(CLK) then
if nCS = '1' then
tmp <= "10101111";
else
tmp <= tmp(PI'high -1 downto PI'low) & '0';
end if;
end if;
end process;

Fourth,

You should use unsigned and signed types instead of std_logic_vector
 
Second,
why do you use falling edge registers?

Because I would like the output to change at the falling edge. This is because the microcontroller is operating at Mode 0 (CPHA = 0 and CPOL = 0) which samples the MISO line at the rising edge.

third,
nCS, is used as a reset.
I wouldn't do that. a reset signal has a very important role in a synchronous design and it's global (same as clock).

Noted. Will include a nReset in every new design of mine.
You should use unsigned and signed types instead of std_logic_vector

Almost all the examples I've seen online use std_logic_vector. What benefits do I gain from unsigned/signed types?
 

Because I would like the output to change at the falling edge. This is because the microcontroller is operating at Mode 0 (CPHA = 0 and CPOL = 0) which samples the MISO line at the rising edge.

CPHA = 0 and CPOL = 0 has nothing to do with the global clock that you use in your design!
they set the parameters for the SCLK (the SPI protocol clock).
The SCLK is always slower than your design's global clock!
Acctually, it's just another signal that the SPI master logic generates (by means of frequency division of the global clock).

Regarding the use of unsigned/signed types instead of SLVs:
VHDL coding tips and tricks: Migrating from std_logic_vector to UNSIGNED or SIGNED data types

You can find KiloTons of infromation about it on this site also.
 
Oh, of course. I totally forgot about the GCLK. I was instead using SCK as my only clock. I understand now what you mean. However, in your code, you only utilize CLK. I've tried modifying it to shift only when SCK is falling. Does this seem OK to you?

Code:
process (CLK,RST)
begin
if (RST='1') then
tmp <= "10101111";
elsif rising_edge(CLK) then
if nCS = '1' then
tmp <= "10101111";
elsif falling_edge(SCK) then
tmp <= tmp(PI'high -1 downto PI'low) & '0';	
end if;
end if;
end process;
 

BIG NO.
You should never use the falling_edge / rising_edge attributes on signals other than your global clock.
Instead, implement a falling edge detector for the SCK signal and use it as an enable for the above process.
It will look like this:


process (CLK,RST)
begin
if (RST='1') then
registered_SCK <= '1' ;
elsif rising_edge(CLK) then
registered_SCK <= SCK ;
end if;
end process;


sck_drop <= (not SCL) and registered_SCK ; -- detecting SCK fall

process (CLK,RST)
begin
if (RST='1') then
tmp <= "10101111";
elsif rising_edge(CLK) then
if nCS = '1' then
tmp <= "10101111";
elsif sck_drop = '1' then
tmp <= tmp(PI'high -1 downto PI'low) & '0';
end if;
end if;
end process;
 
Thank you very much. I will simulate this to understand this better. I have one question - and I know this is a very stupid question - but why do I need a global clock? The CPLD works just fine without a GCLK and it utilizes the SCK for shifting the bits out. I understand this is a very primitive approach, but I'm trying to understand why is it so primitive?

Also is SCL meant to be SCK in your above post?
 
Last edited:

Synchronous design rules.
What works for low speeds won't work for higher speed if you don't design it correctly.
FPGAs and CPLDs are synchronous devices by nature.

Everything happens in relation to the clock.
It's the most important signal in your design. Great care has been practiced by the manufacturer of the device to make sure that the clock gets to all registers at the same time.
If you do not follow these rules your design may work - to a point.
Think of a huge traffic intersection with no signs or traffic lights - cars can pass through. But if they want to avoid accidents they'll have to drive really slow.

---------- Post added at 16:27 ---------- Previous post was at 16:03 ----------

To make the learning experience more productive, I'd do one of the following experiments:

generate 2 SPI designs on the same CPLD (drive the outputs to different pins).
One will follow strict synchronous design rules while the other will incorporate "AVOID" practices.

First, ramp up the speed of the SCK signal and take and see where each design fails.

Then do another test:
lower the frequency to around 10MHz and change the wire that you use for the SCK (if it's not a PCB trace and just a plain conductor).
Take a longer wire and retest both designs. Repeat the test until one fails - and see which one.
 

Thank you very much. I will simulate this to understand this better. I have one question - and I know this is a very stupid question - but why do I need a global clock? The CPLD works just fine without a GCLK and it utilizes the SCK for shifting the bits out.

The short answer is that you might very well not need any other clock, there is nothing inherently wrong with using the SPI clock as long as the PCBA generates a clean clock signal (i.e. no plateaus switches cleanly from low to high and vice versa). However, the places where you can simply use the SPI clock directly are somewhat limited when you're in an FPGA environment. The same comments apply to any notion of a global reset signal as well.

Your example, with the hard coded value to shift out is one of those cases where all you need is the SPI clock and nothing else, your design can work there is no need for any other clock. However, let's take a look at some simple variations on that design:

1. You want to not shift out a constant value, but instead shift out some value provided by some other part of your design. This has several implications that you may not have considered:
- The input 'PI' has to be stable the entire time that you're shifting it out. How does the logic that is creating 'PI' know this? Maybe it is inherent in the system design, but if it's not then what do you think will happen if 'PI' changes at the wrong time?
- As you coded it, it appears your plan is to be sampling 'PI' as part of the async 'reset' time when CS is high. OK, but now think about what this will mean in the implementation. You will have a register with a clock connected to SPI clock; there will need to be asynchronous reset AND preset signals for each bit. There will have to be logic to create these signals, any inadvertant glitch on that logic signal (which you have no control of inside the device) and you'll incorrectly set or clear a bit. You might get it to work, but it would be dicey, you would want to test it over the full temperature and voltage range...and you'd likely have to keep repeating that test every time you change the overall design (not just the SPI portion) because you won't have any real guarantees that something didn't change in the routing.

2. Maybe you want to use this controller to collect data from the SPI controller via SI. Here are some implications of simply using SPI clock
- OK, but again there will be some receiver of the collected data and that receiver will be seeing those bits changing to intermediate values while you're shifting in a new word.
- Does the receiver need to know that you've updated the word? If so, how?
- The receiver of the word is probably running on some other clock, so the output data would need to be synchronized to that clock. If you have multiple receivers they would each have to synchronize it. However, if the SPI controller had access to the clock that everybody else in the design was using, it could simply synchronize it once for all.

I understand this is a very primitive approach, but I'm trying to understand why is it so primitive?
Primitive is probably not the right description, it's just that the situations where simply using the SPI clock are actually useful can be somewhat limited. Because of your simplified first attempt at this, you are not appreciating the troubles that you will run into if you try to expand your design to include other functions as I mentioned above.

Having said all that, there can also be situations where using the system clock and searching for the edges on SPI clock can be a problem. An implicit assumption here is that the system clock is much faster than the SPI clock...what if it's not? It's quite possible that the two clocks are somewhat comparable in speed which means the delay that gets introduced in sampling the input or clocking the output will be too much and the design won't work that way either because you will be violating setup/hold times with the SPI controller. That situation can have several solutions but is maybe off topic here, but thought I'd mention it simply to indicate that there are unstated assumptions built in when you are trying to synchronize two systems.

Kevin Jennings
 

    V

    Points: 2
    Helpful Answer Positive Rating
Thank you very much for your excellent post.

- As you coded it, it appears your plan is to be sampling 'PI' as part of the async 'reset' time when CS is high. OK, but now think about what this will mean in the implementation. You will have a register with a clock connected to SPI clock; there will need to be asynchronous reset AND preset signals for each bit. There will have to be logic to create these signals, any inadvertant glitch on that logic signal (which you have no control of inside the device) and you'll incorrectly set or clear a bit. You might get it to work, but it would be dicey, you would want to test it over the full temperature and voltage range...and you'd likely have to keep repeating that test every time you change the overall design (not just the SPI portion) because you won't have any real guarantees that something didn't change in the routing.

This is something I didn't understand from your post. I am sampling PI whenever nCS is high and PI will be stable the entire time the bits are being shifted out. PI actually comes from a another CPLD which is configured as a Serial In Parallel Out shift register. That CPLD will hold PI till it's nCS goes low and new data is shifted in. When nCS goes back high it will change PI to newly shifted in data.

2. Maybe you want to use this controller to collect data from the SPI controller via SI. Here are some implications of simply using SPI clock
- OK, but again there will be some receiver of the collected data and that receiver will be seeing those bits changing to intermediate values while you're shifting in a new word.
- Does the receiver need to know that you've updated the word? If so, how?
- The receiver of the word is probably running on some other clock, so the output data would need to be synchronized to that clock. If you have multiple receivers they would each have to synchronize it. However, if the SPI controller had access to the clock that everybody else in the design was using, it could simply synchronize it once for all.

I am sorta confused on what you meant by "receiver" in the above. Eventually, I would like to transfer some data to this SPI slave, yes. But I lose you when you say there will be some receiver of the collected data. I assume by receiver you meant the shift registered I talked about in the first post? If so, data/instructions that I will send to this device will not be passed on anywhere else at all. However, as is obvious from my first post, this device collects data from PI and sends it over to the SPI Master.
 

This is something I didn't understand from your post. I am sampling PI whenever nCS is high and PI will be stable the entire time the bits are being shifted out. PI actually comes from a another CPLD which is configured as a Serial In Parallel Out shift register. That CPLD will hold PI till it's nCS goes low and new data is shifted in. When nCS goes back high it will change PI to newly shifted in data.
Is the nCS of the other CPLD the same nCS of your SPI controller? If so, then yes you should be OK since that will prevent PI from changing while it is being shifted out. It does bring into question though why you have a CPLD to convert a serial stream into parallel and then this controller which then converts the parallel back into serial...but that's a different topic, presumably something else on the board needs to work with the parallel PI at some point.

I am sorta confused on what you meant by "receiver" in the above. Eventually, I would like to transfer some data to this SPI slave, yes. But I lose you when you say there will be some receiver of the collected data.
The 'receiver' is whatever other logic you have in this design that would then work with the data that you collect. For example, let's say the SPI controller shifts in 8 bits to you via SI. You dutifully convert that into an 8 bit number call it 'XYZ' and that 'XYZ' is an output of your SPI controller entity. Presumably there is some other entity in the design that will take 'XYZ' and do something with it; that other entity would be the thing that is the receiver of 'XYZ'.

I assume by receiver you meant the shift registered I talked about in the first post?

Unless I missed it, I don't think you used SI at all in your earlier posts so we are talking about different things, hopefully cleared up by my previous paragraph.
If so, data/instructions that I will send to this device will not be passed on anywhere else at all.
If you're not going to use it then why would you collect it? What you're saying then is that the 'XYZ' signal that I mentioned above and would then be the output of your SPI controller entity would be left unconnected. If that is the really the case, then synthesis would completely remove 'XYZ' since it is not used.

Kevin Jennings
 
Is the nCS of the other CPLD the same nCS of your SPI controller? If so, then yes you should be OK since that will prevent PI from changing while it is being shifted out. It does bring into question though why you have a CPLD to convert a serial stream into parallel and then this controller which then converts the parallel back into serial...but that's a different topic, presumably something else on the board needs to work with the parallel PI at some point.

I'll elaborate on my design. I am designing a board which tests the continuity of wiring harnesses. The purpose of the CPLDs (let's call this Driver) is to drive a test vector onto the harness and the other CPLD reads the test vector back (lets call this Receiver). The microcontroller is in charge of the entire process - it clocks out a test vector onto the Driver via SPI and then deselects it. It then selects the Receiver, which is connected in parallel to the Driver via the wiring harness, and clocks out the resultant vector. That's really all there is to it. The rest of the work is done by the microcontroller (i.e. comparing the resultant vector with the expected one, etc.). Of course the CPLDs are protected by TVS diodes against ESD and uses buffers in open-drain configuration to avoid bus contention in case a short circuit is present on the wiring harness.

This approach has worked well for us as it can detect open and short circuits. It obviously does not take into account if the resistance of the wires is high or not, etc. But that's not a a design goal for us.

In total, I have three nCS (Chip Selects) on my microcontroller. One is for the Driver, the 2nd is for the Receiver and the third is for an SD Card. I have already modified my code to utilize clock sync. and edge detection instead of just relying on SCK so I think I will avoid the issues you mentioned. My next hope is to add some sort of checksum to the received vector. The Receiver CPLD can calculate a checksum and send it along with the data. The microcontroller can evaluate this and see if the data transmission was valid.

Is this sort of approach worthwhile? Would it increase the reliability of the design? My inexpeirence suggests it's probably a good approach but I'm not sure why if any glitches could occur. The speed of transmission isn't very high - just 1 Mhz. The CPLDs are placed just 1" from the microcontroller.
 
Last edited:

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top