I've implemented a 8-bit Parallel in Serial Out (PISO) Shift register in VHDL on my Max V CPLD. I'm using SPI to interface with the CPLD using my AVR. The circuit works but only partially. Suppose I have an input which contains only one set bit i.e. 00010000 or 10000000 etc. In this case, the AVR reads in the data correctly.
However, if the input to the PISO register is 10100000, the AVR reads in the data as 11100000. This is also reflected in my oscilloscope. I've attached the picture to illustrate this.
The top waveform is the serial output from my shift register and the bottom waveform is the clock. Shouldn't the serial output become 0 on the 2nd clock cycle?
If the data is 10000001, then its read in as 10000011. What could be causing this? Thinking it could be my VHDL code, I've even copy pasted other people's codes from the web to see if it works but unfortunately I get the same results.
The following is the code for my AVR
int main(void)
{
DDRB = (1 << DD_MOSI) | (1 << DD_SCK) | (1 << 2) | (1 << 0);
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0) | (1 << SPR1);
DDRD = 0xFF;
PORTD = 0x00;
char datain;
while(1)
{
PORTB = 1; // This loads the shift register.
_delay_ms(10);
PORTB = 0x00;
_delay_ms(10);
SPDR = 0b01011101;
while(!(SPSR & (1 << SPIF)));
datain = SPDR;
_delay_ms(10);
PORTD = datain;
}
return 1;
}
And this is the VHDL code I'm using. Note that I've intentionally left out the input. I'm hardcoding the input as then I don't have to mess around with additional wires. Which I was hoping would provide less room for error.
library ieee;
use ieee.std_logic_1164.all;
entity PISO is
port(C, CS_N : in std_logic;
--D : in std_logic_vector(7 downto 0);
SO : out std_logic);
end PISO;
architecture archi of PISO is
signal tmp: std_logic_vector(7 downto 0);
begin
process (C, CS_N)
begin
if CS_N = '1' then
tmp <= "10000001";
elsif rising_edge(C) then
tmp <= tmp(tmp'high - 1 downto tmp'low) & '0';
end if;
end process;
SO <= tmp(7);
end archi;
Note that I'm quite new to VHDL/CPLDs and I keep running across something regarding timings and how critical they are. I've tried to read up on them but don't seem to quite get it. I understand gate delays etc. but how am I supposed to set up the CPLD such that this is not an issue? My clock is not very fast. I'm running it at the lowest possible frequency i.e. 62.5kHz. Could this error have something to do with breadboards? I've run out of photoresist copper clads so can't make a PCB just yet. The Max V is on a development board.
I'll appreciate any sort of pointers on how to solve this issue. I've been at it for the past 8 hours or so to no avail.
ADDED INFO: Initially, when I programmed the CPLD I tested it via the buttons on my development board. That showed no problems whatsoever and the register spat out bits serially just fine. Not only that, I've even clocked it via the AVR. That worked great as well. It was only after I confirmed that it was working ok that I started work on the SPI interface which is where things went wrong.
New Waveforms:
OK, here we go. For all screenshots, the serial out was the same (10000001). I've made a slight improvement such that 10000001 no longer comes out as 10000011 but instead is 10000010. When I was debugging before, I inadvertently set CPHA bit to 1. When I set it back to 0, the data was 100000010 instead of 10000011. Still not correct, of course. Here are the waveforms from the scope.
Top waveform: Serial Out
Bottom waveform: Clock. Triggered at rising edge.
Top waveform: Chip Select (Active Low)
Bottom waveform: Clock. Triggered at rising edge.
Top waveform: Chip Select
Bottom waveform: Clock. Triggered at falling edge.
Top waveform: Serial Out.
Bottom waveform: Clock. Triggered at falling edge.
EDIT: Forgot to add, this is my cleaned up AVR code now.
while(1)
{
bit_clear(PORTB,BIT(CS)); // Low Chip Select
SPDR = 0xFF;
while(!(SPSR & (1 << SPIF)));
datain = SPDR;
PORTD = datain;
bit_set(PORTB,BIT(CS)); // High Chip select
_delay_us(100); // Keep high for 100us. This made it easier to see it on o-scope.
}