Soy nuevo en la programación de VHDL y FPGA, y aunque conozco un buen número de problemas que pueden existir entre la simulación y la síntesis, este problema en particular me dejó perplejo.
Mi diseño es bastante simple:
-
El bus SPI acepta un byte de datos del modelo de arriba, que luego se desplaza en los bordes crecientes del reloj.
-
Al restablecerse, todas las señales se configuran en valores iniciales. Activo esta entrada conectando un cable entre el pin de reinicio y la conexión a tierra en mi FPGA.
-
La interfaz SPI luego ingresa a una etapa de "espera" donde espera una señal del modelo anterior, que siempre es alta, por lo que sale de esta etapa de espera dentro de un ciclo de reloj.
-
Luego entra en una etapa de "escritura" en la que, en el primer flanco ascendente de la entrada del reloj, escribe el siguiente bit desde su entrada de datos a su salida de datos, y escribe la salida del reloj a baja. En el siguiente borde del reloj, escribe que la salida del reloj es alta, desplazando los datos a cualquier dispositivo que lo esté esperando conectado a los pines de salida del FPGA, e incrementa el contador que mantiene un registro del bit de entrada del byte de entrada que está configurando siguiente ciclo de entrada de reloj.
-
Este patrón se repite, hasta que el reloj de salida se establece en alto, cambiando el bit de entrada final, e incrementa el contador de selección de bit de entrada a 8. El siguiente flanco ascendente del reloj de entrada el dispositivo restablece todas las variables y cambia al modo "en espera", en lugar de intentar generar el bit de entrada # 8 del byte de entrada, que no existe, ya que la matriz de bytes de entrada es un bit_vector con índices etiquetados del 0 al 7.
-
Como esta versión mantiene constante el byte de entrada así como constante el bit de activación de entrada, el dispositivo repite y repite su salida de forma indefinida.
Todo esto funciona en la simulación como se espera, y casi funciona en realidad como se espera, excepto por el cambio final de los datos. Cuando subo el programa al FPGA, parece que se omite el paso de escribir la salida del reloj en el octavo bit, y en su lugar, se salta directamente a la fase de reinicio y se reinicia en la fase de "espera" el siguiente ciclo de entrada de reloj. Establece bien la salida de datos, pero luego, en lugar de configurar la salida del reloj alta por última vez antes de reiniciar, simplemente salta para reiniciar.
Creo que esto tiene que ver con el hecho de que incrementa el contador del selector de bits de entrada a 8 al mismo tiempo que escribe el reloj de salida alto. En lugar de esperar hasta el próximo ciclo de reloj de entrada para darse cuenta de que este valor es ahora 8, y que debería restablecerse, por alguna razón, ve el cambio a 8 de inmediato y decide restablecerse ya que el contador ahora está en 8.
Una nota final, en mi código hay un "bit de estado" en la arquitectura de la interfaz SPI que no está siendo utilizado por la arquitectura anterior en este diseño, sin embargo, en diseños futuros será necesario incluirlo. Yo simplificaría este código de depuración eliminándolo; sin embargo, cuando se elimina de todos los niveles de implementación, el error que enumeré anteriormente no se produce, y el FPGA se comporta tan perfectamente como la simulación. No tengo idea de por qué esto es.
Aquí está mi código:
El nivel más bajo en el diseño, la interfaz SPI:
entity SPI is
port (data_out, clk_out, status_out : out bit; data_in : in bit_vector(0 to 7); CLOCK, begin_in, RESET: in bit);
end entity SPI;
architecture SPIArch of SPI is
type SPIState is (waiting, writing);
signal current_state : SPIState := waiting;
subtype byteCount is integer range 0 to 8;
signal current_bit : integer := 0;
signal data_set : bit := '0';
begin
shiftOut : process (CLOCK, RESET)
begin
if (RESET = '0') then
current_bit <= 0;
data_set <= '0';
clk_out <= '0';
data_out <= '0';
status_out <= '1';
current_state <= waiting;
elsif (CLOCK = '1' and CLOCK'event) then
case current_state is
when waiting =>
if (begin_in = '1') then
status_out <= '0';
current_state <= writing;
end if;
when writing =>
if (current_bit /= 8) then
if (data_set = '1') then
clk_out <= '1';
data_set <= '0';
current_bit <= current_bit + 1;
end if;
if (data_set = '0') then
data_out <= data_in(current_bit);
clk_out <= '0';
data_set <= '1';
end if;
else
data_out <= '0';
clk_out <= '0';
current_bit <= 0;
status_out <= '1';
data_set <= '0';
current_state <= waiting;
end if;
end case;
end if;
end process shiftOut;
end architecture SPIArch;
A continuación, el nivel por encima de la interfaz SPI, el "controlador" para alimentar a la interfaz SPI el valor de prueba internamente (tenga en cuenta que se usó el divisor de reloj grande para poder ver lo que estaba pasando en las salidas de mi FPGA mediante LED) :
entity SPIDrive is
port (data_out, clk_out, status_out : out bit; CLOCK, RESET: in bit);
end entity SPIDrive;
architecture SPIDriveArch of SPIDrive is
signal SPI_clk : bit;
signal SPI_counter : integer;
signal SPI_data : bit_vector(0 to 7);
signal SPI_begin : bit;
begin
testDevice : entity work.SPI(SPIArch)
port map (data_out, clk_out, status_out, SPI_data, SPI_clk, SPI_begin, RESET);
outputTest : process (CLOCK, RESET)
begin
if (RESET = '0') then
SPI_clk <= '0';
SPI_counter <= 0;
SPI_data <= B"10110011";
SPI_begin <= '1';
elsif (CLOCK = '1' and CLOCK'event) then
SPI_counter <= SPI_counter + 1;
if (SPI_counter = 5000000) then
SPI_counter <= 0;
SPI_clk <= not SPI_clk;
end if;
end if;
end process outputTest;
end architecture SPIDriveArch;
El nivel final, utilizado solo como banco de pruebas para la simulación, y simplemente genera la fuente del reloj y el reinicio, que normalmente proviene de un oscilador integrado y un pin de entrada:
entity testbench1 is
end entity testbench1;
architecture testbench1Arch of testbench1 is
signal data_out, clk_out, status_out, CLOCK, RESET: bit;
begin
troll : entity work.SPIDrive(SPIDriveArch)
port map (data_out, clk_out, status_out, CLOCK, RESET);
process
begin
RESET <= '0';
CLOCK <= '0';
wait for 1 ns;
RESET <= '1';
while (true) loop
wait for 1 ns;
CLOCK <= '1';
wait for 1 ns;
CLOCK <= '0';
end loop;
end process;
end architecture testbench1Arch;
Aquí está la salida de mi simulación (Nótese que cambié el divisor del reloj en el código del "controlador SPI" a 10 en lugar de 5 millones para facilitar la visualización en la simulación, y que disparo un reinicio de 1 ns en la simulación ( como se ve en mi código de banco de pruebas) (aunque es difícil de ver en la imagen):
Paraevitarcualquierconfusiónencuantoacuálfuemierror,aquíhayunaversióneditada(pobremente)delaimagendesimulación,quemuestrabásicamenteloqueveoenlavidarealprovenientedemiFPGA:
Unaadiciónfinalprobablementeinnecesaria,recibounagrancantidaddeadvertenciasaparentementeignorablesdurantelasíntesis(porejemplo,unagrancantidaddepestillosenmientradadedatos,locualtienesentidoyaquenuncacambioelvalordeentradadedatos)sinembargo,tambiénobtengotresqueindican"sin restricciones de diseño" en mis archivos. Supongo que estos son para algún tipo de simulación y no son la causa de mis problemas, sin embargo, parecen ser notables, así que los menciono.
Como se indica en el título, estoy usando Lattice Diamond para la programación y simulación (aunque la simulación abre Active-HDL) y el Lattice MACHXOLF-6900C FPGA