Además del rango nulo señalado por Brian y Eugene, la implementación de su algoritmo no funcionará porque depende de que los valores de actualización de la señal ocurran en el mismo ciclo delta. No se actualiza ninguna señal durante la ejecución de un proceso, un nuevo valor de señal asignado está disponible en ciclos de simulación subsiguientes, a diferencia de la asignación de variables que surte efecto inmediatamente.
Hay dos formas de abordar esto: introducir el retraso entre cada vez que se asigna una señal y cuándo se usa o utiliza las variables a continuación.
(En realidad, hay una tercera forma, evaluar antes de asignar, pero VHDL está diseñado específicamente para que no tenga que hacerlo. Los ciclos de simulación subsiguientes (ciclos delta) permiten la emulación del paralelismo y se distinguen por no estar precedidos por el avance de la simulación tiempo.)
Esto demuestra cómo usar las variables:
-- logic taken from https://en.wikipedia.org/wiki/division_algorithm
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity div is
port(
operanda: in std_logic_vector(3 downto 0);
operandb: in std_logic_vector(3 downto 0);
errorsig: out std_logic := '0';
result_low: out std_logic_vector(3 downto 0);
result_high: out std_logic_vector(3 downto 0)
);
end div;
architecture foo of div is
begin
UNLABELED:
process(operanda,operandb)
variable quotient: unsigned (3 downto 0);
variable remainder: unsigned (3 downto 0);
begin
-- if D == 0 then error(DivisionByZeroException) end
-- Q := 0 -- initialize quotient and remainder to zero
-- R := 0
-- for i = n-1...0 do -- where n is number of bits in N
-- R := R << 1 -- left-shift R by 1 bit
-- R(0) := N(i) -- set the least-significant bit of R equal to bit i of the numerator
-- if R >= D then
-- R := R - D
-- Q(i) := 1
-- end
-- end
-- We
errorsig <= '0'; -- allows successive operations
if operandb = "0000" then
-- i<= 0;
assert operandb /= "0000"
report "Division by Zero Exception"
severity ERROR;
errorsig <= '1';
else
quotient := (others => '0'); -- "0000"
remainder := (others => '0');
for i in 3 downto 0 loop
remainder := remainder (2 downto 0) & '0'; -- r << 1
remainder(0) := operanda(i); -- operanda is numerator
if remainder >= unsigned(operandb) then -- operandb denominator
remainder := remainder - unsigned(operandb);
quotient(i) := '1';
end if;
end loop;
result_high <= std_logic_vector(quotient); -- for error keeps
result_low <= std_logic_vector(remainder); -- last value (invalid)
end if;
end process;
end architecture foo;
library ieee;
use ieee.std_logic_1164.all;
entity div_tb is
end entity;
architecture foo of div_tb is
signal operanda: std_logic_vector (3 downto 0) := (others => '0');
signal operandb: std_logic_vector (3 downto 0) := (others => '1');
signal errorsig: std_logic;
signal result_low: std_logic_vector (3 downto 0); -- remainder
signal result_high: std_logic_vector (3 downto 0); -- quotient
begin
DUT:
entity work.div
port map (
operanda => operanda,
operandb => operandb,
errorsig => errorsig,
result_low => result_low,
result_high => result_high
);
STIMULUS:
process
begin
operanda <= "1000"; -- 8
operandb <= "0010"; -- 2
wait for 20 ns;
operandb <= "0100"; -- 4
wait for 20 ns;
operandb <= "1000"; -- 8
wait for 20 ns;
operanda <= "1111"; -- 15
operandb <= "0011"; -- 3
wait for 20 ns;
operandb <= (others => '0');
wait for 20 ns;
operanda <= "1101"; -- 13
operandb <= "0111"; -- 7
wait for 20 ns;
wait;
end process;
end architecture;
Y cuando se ejecuta incluso funciona:
(clickable)
Usarunasubrutinahaciendounarestaenlugardeunoperador
Podemosverlasdosfuncionesinvolucradasenelpaquetenumeric_std:
function"-" (L, R: UNSIGNED) return UNSIGNED is
constant SIZE: NATURAL := MAX(L'LENGTH, R'LENGTH);
variable L01 : UNSIGNED(SIZE-1 downto 0);
variable R01 : UNSIGNED(SIZE-1 downto 0);
begin
if ((L'LENGTH < 1) or (R'LENGTH < 1)) then return NAU;
end if;
L01 := TO_01(RESIZE(L, SIZE), 'X');
if (L01(L01'LEFT)='X') then return L01;
end if;
R01 := TO_01(RESIZE(R, SIZE), 'X');
if (R01(R01'LEFT)='X') then return R01;
end if;
return ADD_UNSIGNED(L01, not(R01), '1');
end "-";
function ADD_UNSIGNED (L, R: UNSIGNED; C: STD_LOGIC) return UNSIGNED is
constant L_LEFT: INTEGER := L'LENGTH-1;
alias XL: UNSIGNED(L_LEFT downto 0) is L;
alias XR: UNSIGNED(L_LEFT downto 0) is R;
variable RESULT: UNSIGNED(L_LEFT downto 0);
variable CBIT: STD_LOGIC := C;
begin
for I in 0 to L_LEFT loop
RESULT(I) := CBIT xor XL(I) xor XR(I);
CBIT := (CBIT and XL(I)) or (CBIT and XR(I)) or (XL(I) and XR(I));
end loop;
return RESULT;
end ADD_UNSIGNED;
Primero vamos a llamar a la función sub
. No necesitamos redimensionar ambos operandos son del mismo tamaño. Necesitamos copias de los parámetros y podemos consolidar los bits que necesitamos:
architecture foo of div is
function sub (L, R: unsigned) return unsigned is
variable L01: unsigned(L'LENGTH - 1 downto 0);
variable R01: unsigned(R'LENGTH - 1 downto 0);
variable CBIT: std_logic := '1'; -- carry in '1'
variable RESULT: unsigned(L01'RANGE);
begin
L01 := TO_01(L,'X');
R01 := not TO_01(R,'X');
for i in 0 to integer(L01'LENGTH) - 1 loop
RESULT(i) := CBIT xor L01(i) xor R01(i);
CBIT := (CBIT and L01(i)) or (CBIT and R01(i)) or (L01(i) and R01(i));
end loop;
return RESULT;
end function;
(Y lo hicimos como un elemento declarativo de bloque en la declaración de arquitectura).
Luego cambie a la función sub
:
-- remainder := remainder - unsigned(operandb);
remainder := sub(remainder, unsigned(operandb));
Y todo sin usar declaraciones de espera o agregar operadores:
(clickable)
Yestodalamismarespuestaqueusareloperador"-"
. (Tenga en cuenta que la raíz de OperandA
se ha cambiado a decimal).
Podría notar que hay un poco más de optimización que se puede hacer con una función local sub
. Podríamos deshacernos de la cláusula de uso del paquete numeric_std
y convertir cualquier cosa que sea unsigned
a std_logic_vector
. Necesitaría una función TO_01
(o para que los bucles realicen lo mismo) para std_logic_vector
.
También podría simplemente funcionar un poco más la función sub
. (Sugerencia: implica eliminar la funcionalidad, implícita en and_table
, or_table
y xor_table
en el paquete std_logic_1164
body. ¿Realmente necesitamos TO_01
? El propósito del paquete numeric_std
es representar números binarios en el tipo unsigned
.) En otras palabras, podría escribir su propia función de resta.
Si activa algo con una función similar, se le pedirá que explique cómo funciona.