Si tiene que dividir una operación en varios ciclos de reloj, tiene dos opciones: canalización y secuenciación
Consideremos una operación mítica que consiste en cuatro multiplicaciones, por ejemplo, donde cada multiplicación (excepto la primera) requiere la salida de la multiplicación anterior como una de sus entradas. Sin embargo, las ideas básicas son mucho más generales.
En la canalización, tiene suficiente hardware para realizar cada operación simultáneamente, interconectado por registros de tubería. Esto implica cuatro multiplicadores, separados por registros de tubería. Tomará 4 ciclos de reloj para obtener el primer resultado (por lo que decimos que la tubería tiene 4 etapas de profundidad y la latencia es de 4 ciclos) pero luego obtiene un nuevo resultado en cada ciclo de reloj (por lo que decimos que la tasa de rendimiento es de 1 ciclo) . Un poco más de información sobre el diseño de tuberías. ..
Desventaja: esta es una gran pieza de hardware: 4 multiplicadores son relativamente caros (es por eso que algunas familias de FPGA ofrecen muchos pequeños multiplicadores como bloques altamente optimizados).
La alternativa es secuenciar cada operación en el mismo multiplicador, dando un diseño mucho más pequeño, pero entregando un resultado cada 4 ciclos.
En este caso, puede usar un solo multiplicador, almacenando su resultado en un solo registro, para un diseño mucho más pequeño.
Cada 4º ciclo (o cuando algo más indique que hay una nueva entrada lista) conectas la nueva entrada a un puerto de entrada del multiplicador; en otros ciclos, alimenta ese puerto desde el registro de salida (para utilizar el resultado multiplicado anterior). Y en cada ciclo alimenta los datos apropiados (coeficientes de filtro, valores de matriz, lo que sea) en el otro puerto multiplicador. Cuatro ciclos más tarde, presenta el resultado final como su salida y le indica a su consumidor que un nuevo resultado está listo.
La forma obvia de secuenciar estas operaciones es una máquina de estado (FSM).
De hecho, los cálculos se pueden incrustar en las acciones asociadas con cada estado, por ejemplo:
if rising_edge(clk) then
Done <= '0'; -- and any other default actions
case state is
when Idle =>
if Start = '1' then
Temp := Input * C1;
State := S1;
end if;
when s1 =>
Temp := Temp * C2;
State := S2;
when s2 =>
Temp := Temp * C3;
State := S3;
when s3 =>
Temp := Temp * C4;
State := S4;
when s4 =>
Output <= Temp;
Done <= '1';
State := Idle;
-- optional alternative for bombproof handshaking
-- if Start = '0' then
-- Done <= '1';
-- State <= Idle;
-- end if;
end case;
end if;
Si está interactuando con otras unidades: interfaces SPI, UART, etc., el FSM suele ser el mejor método.