Implementé un registro de desplazamiento de salida en serie (PISO) paralelo de 8 bits en VHDL en mi Max V CPLD. Estoy usando SPI para interactuar con el CPLD usando mi AVR. El circuito funciona pero solo parcialmente. Supongamos que tengo una entrada que contiene solo un bit establecido, es decir, 00010000 o 10000000, etc. En este caso, el AVR lee los datos correctamente.
Sin embargo, si la entrada al registro PISO es 10100000, el AVR lee los datos como 11100000. Esto también se refleja en mi osciloscopio. Adjunto la imagen para ilustrar esto.
La forma de onda superior es la salida en serie de mi registro de desplazamiento y la forma de onda inferior es el reloj. ¿No debería la salida en serie convertirse en 0 en el segundo ciclo de reloj?
Si los datos son 10000001, entonces se leen como 10000011. ¿Qué podría estar causando esto? Pensando que podría ser mi código VHDL, incluso copié y pegué los códigos de otras personas de la web para ver si funciona, pero desafortunadamente obtengo los mismos resultados.
El siguiente es el código para mi 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;
}
Y este es el código VHDL que estoy usando. Tenga en cuenta que he omitido intencionalmente la entrada. Estoy codificando la entrada ya que entonces no tengo que perder el tiempo con cables adicionales. Lo cual esperaba proporcionaría menos margen de 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;
Tenga en cuenta que soy bastante nuevo en VHDL/CPLD y sigo encontrando algo con respecto a los tiempos y cuán críticos son. He intentado leer sobre ellos, pero parece que no lo entiendo del todo. Entiendo los retrasos en las puertas, etc., pero ¿cómo se supone que debo configurar el CPLD para que esto no sea un problema? Mi reloj no es muy rápido. Lo estoy ejecutando en la frecuencia más baja posible, es decir, 62,5 kHz. ¿Podría este error tener algo que ver con las placas de prueba? Me he quedado sin revestimientos de cobre fotorresistentes, por lo que todavía no puedo hacer una PCB. El Max V está en una placa de desarrollo.
Agradeceré cualquier tipo de sugerencia sobre cómo resolver este problema. Estuve en eso durante las últimas 8 horas más o menos sin éxito.
INFORMACIÓN ADICIONAL: Inicialmente, cuando programé el CPLD, lo probé mediante los botones de mi placa de desarrollo. Eso no mostró ningún problema y el registro escupió bits en serie muy bien. No solo eso, incluso lo he cronometrado a través del AVR. Eso funcionó muy bien también. Solo después de confirmar que funcionaba bien, comencé a trabajar en la interfaz SPI, que es donde las cosas salieron mal.
Nuevas formas de onda:
Está bien, aquí vamos. Para todas las capturas de pantalla, la salida de serie fue la misma (10000001). Hice una ligera mejora, de modo que 10000001 ya no aparece como 10000011 sino como 10000010. Cuando estaba depurando antes, inadvertidamente configuré el bit CPHA en 1. Cuando lo configuré de nuevo en 0, los datos eran 100000010 en lugar de 10000011 Todavía no es correcto, por supuesto. Aquí están las formas de onda del osciloscopio.
Forma de onda superior: Salida serie
Forma de onda inferior: Reloj. Activado en el flanco ascendente.
Forma de onda superior: Chip Select (activo bajo)
Forma de onda inferior: Reloj. Activado en el flanco ascendente.
Forma de onda superior: Chip Select
Forma de onda inferior: Reloj. Activado en el borde descendente.
Forma de onda superior: Salida serie.
Forma de onda inferior: Reloj. Activado en el borde descendente.
EDITAR: Olvidé agregar, este es mi código AVR limpio ahora.
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.
}
Bueno, echemos un vistazo a su código VHDL. Tenga en cuenta que no estoy tratando de ser crítico o duro, solo estoy tratando de ponerlo al día rápidamente. Su código actualmente es este:
process (C, ALOAD)
begin
if (ALOAD='1') then
tmp <= "10000001";
elsif (C'event and C='1') then
tmp <= tmp(6 downto 0) & '0';
SO <= tmp(7);
end if;
end process;
Pero debería verse así:
process (C, ALOAD)
begin
if ALOAD='1' then
tmp <= "10000001";
elsif rising_edge(C) then
tmp <= tmp(tmp'high-1 downto tmp'low) & "0"; -- '0' and "0" are equivalent in this context
end if;
end process;
SO <= tmp(tmp'high);
Los cambios que hice son menores, sin duda, pero importantes.
Lo primero es mi uso de "rising_edge()". Esto es algo nuevo para VHDL y no está cubierto en algunos de los libros de VHDL. Esto se considera mejor que usar 'event. Del mismo modo, hay un borde cayendo ().
Lo siguiente es mi uso de 'alto y' bajo en lugar de codificar los valores. Esto realmente no importa para este código, pero cuando comiences a hacer cosas más grandes, te ayudarán mucho. Por ejemplo, podría simplemente cambiar la definición de tmp para que sea más grande y el resto del código se ajustará automáticamente (excepto la inicialización a "10000001").
También debo señalar que se desaconseja un reinicio asíncrono o una carga en FPGA , pero está bien en CPLD.
También tenga en cuenta que moví la asignación de SO fuera del proceso. Esto puede o no ser lo que pretendías. De la forma en que lo tiene, hay un flip-flop adicional que va de tmp (7) a SO. Normalmente, con SPI, esto no es lo que desea porque el reloj SPI podría desaparecer al final de la transferencia y nunca obtendrá ese último bit. Por otro lado, con la forma en que lo hice, comenzará a obtener ese primer bit cuando ALOAD = '1', no en el borde ascendente de C.
Desafortunadamente, esto realmente no responde a su pregunta sobre por qué está recibiendo datos incorrectos en el AVR. Simplemente no hay suficiente información en su pregunta. Este es el tipo de cosas que miraría o me preocuparía:
Su imagen o-scope no muestra las otras señales spi, como CS. CS es fundamental para comprender dónde se supone que deben ir los bits. Además, saber cuál es el modo SPI ayudará con esto.
En la imagen del oscopio, el primer ciclo de reloj donde SO='1' NO es el primer bit de la transferencia SPI. Cargó el registro de desplazamiento 10-20 mS antes de eso, y su período de reloj es de aproximadamente 20 uS. Entonces tenía al menos 1 ciclo de reloj antes de SO='1', y probablemente más. Entonces, aquí están sucediendo algunas cosas extrañas: no tenemos suficiente información para comprender el comportamiento.
Está usando ALOAD para cargar el registro de desplazamiento, pero normalmente usaría CS_N (bajo activo). Mientras que CS_N='1' realiza una carga asíncrona del registro de desplazamiento, mientras que CS_N='0' lo desplaza. Usar ALOAD como el que tiene aquí está bien, pero probablemente no es lo que quería y no funciona con lo que parece ser un reloj SPI extraño (del punto anterior).
Entonces, esto es lo que debes hacer...
Limpie un poco el VHDL. Vuelva a publicar la versión actualizada. Dado que su alcance solo tiene 2 canales, conecte un canal a CS_N y el otro canal a CLK. Gatillo en el flanco descendente de CLK. Capture una forma de onda que muestre 2 relojes antes del flanco descendente de CLK y 5 relojes después. Sin cambiar la configuración del osciloscopio , elimine CLK y coloque la sonda en SO. Captura otra imagen. Repita esto para el flanco ascendente de CLK. Entonces, 4 imágenes de forma de onda en total.
Haz eso y podemos reevaluar lo que podría estar mal.
Editar: Actualizado para reflejar la pregunta actualizada.
Veo dos problemas: primero, si el AVR está muestreando en el flanco ascendente del reloj, debe marcar su registro de desplazamiento fuera del flanco descendente. Como mencionó Supercat, esto le dará +/- 0.5 períodos de reloj de configuración y retención en su AVR.
Y segundo: como mencionaste, estás obteniendo 10000010 en lugar de 10000001. No creo que tu código VHDL sea erróneo en este, pero obviamente está saliendo mal del CPLD. Si tuviera que adivinar, diría que el problema se debe a algunos problemas de integridad de la señal con su CLK. Es difícil saberlo con su alcance, pero parece que tiene más de un voltio de sobreimpulso y subimpulso en esa señal (y con eso vendría mucho zumbido). Ese timbre, si es lo suficientemente malo, podría hacer que el CPLD "doble el reloj", lo que significa ejecutar el registro de desplazamiento dos veces para un solo borde del reloj. Y si es _realmente_malo_ podría hacer que el CPLD se enganche y explote literalmente (lo he visto suceder).
Aquí hay algunos experimentos para probar:
En lugar de 10000001, use 10101010 o 01010101. Esto lo ayudará a ver qué bit se duplica y si siempre es el mismo bit.
Acérquese a los bordes del reloj con el visor. Asegúrese de que sus sondas de alcance estén en el CPLD, no en el AVR, cuando haga esto. Sí, hace una GRAN diferencia.
Asumiendo que hay un sobreimpulso o un subimpulso y suena en el reloj, la solución es agregar una terminación de señal adecuada en la línea. Comenzaría con una resistencia en serie de 50 ohmios en el AVR. Nota: esto ralentizará los bordes del reloj, pero como está cronometrando el CPLD en el borde descendente, tiene mucho tiempo disponible.
¿Cómo funciona el reloj de AVR a CPLD? ¿Un cable largo entre PCB? Esa sería mi conjetura.
darrón
olin lathrop