sda*_*aau 4 logic counter integer vhdl twos-complement
从本质上讲,我的问题是:" 这样做不容易吗? "; 这个是什么,如下所示(代码也是如此):
我希望有一种"补充"计数器功能,用VHDL实现,它基本上会反转/补充/不是每一步中的计数器值,为测试提供稍微丰富的位模式.当然,我希望这可以合成(因此计数器值可能分配给引脚)和可移植代码(即仅实现IEEE库,否STD_LOGIC_ARITH).我也不希望默认将all视为无符号(所以我想避免STD_LOGIC_UNSIGNED).
简而言之,这个计数器可以描述为:给定初始值C [0],那么每个时钟滴答的值将是:
C[i+1] = not(C[i]) + ( ( C[i]<(Cmax/2) ) ? 0 : 1 )
Run Code Online (Sandbox Code Playgroud)
...或者给定C是16位宽(这将导致无符号Cmax = 65535和Cmax/2 = 32768),它也可以写成:
C[i+1] = 65535 - C[i] + ( ( C[i]<32768 ) ? 0 : 1 )
Run Code Online (Sandbox Code Playgroud)
这里的技巧是计数器只应增加一次 - 如果它对互补和"正常"范围都增加,那么就不会发生变化(方程将在两个值之间"振荡").
因此,考虑到检查C [i] <(Cmax/2)与检查C的最重要(第15位)基本相同,我认为我可以使用以下内容在VHDL中轻松实现类似的内容:
Y <= not(Y) + Y(15);
Run Code Online (Sandbox Code Playgroud)
男孩,我错了"轻松":)
第一个问题是上述等式有可能最终达到65535 + 1,在这种情况下,结果将需要17位(即溢出); 在我的情况下,我只想截断/忽略任何'进位'.
这导致了使用什么的问题:
std_logic_vector有补充not()定义; 但它没有+(添加)定义natural/ integer可能在内部占用32位,因此它们的位宽不是necesarilly指定的; 他们支持算术+,但没有补充not()unsigned,也有一些问题(不记得哪个)第15个(MSB)位只能在Y为0时提取std_logic_vector,在这种情况下,Y(15)是单个std_logic- 但是,它需要转换为integer类型,因为否则+不会定义加法:
所以,我目前的解决方案(下面)首先有两个计数器寄存器副本; 一个是SIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0); 另一个是SIGNAL tmp_na : natural.然后:
natural变量执行(从副本复制STD_LOGIC_VECTOR)std_logic只能转换为integer如果它转换为std_logic_vector第一个(我很幸运能vectorize在网上找到该功能).这里最糟糕的部分是如何反馈natural变量值,回到STD_LOGIC_VECTOR一个变量值; 我能构建的唯一工作命令是:
wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));
Run Code Online (Sandbox Code Playgroud)
...; 但请注意,此命令基本上"设置"该值,该值将在下次运行此命令时"实现" .因此,它不能在'计数器'时钟过程中运行 - 在下面的代码中,我在更快的'主'时钟过程中使用它.
最后,下面的代码确实有效(通过ISE WebPack中的行为模拟) - 但是,我仍然想知道是否有更简单的方法来解决这个问题.
提前感谢任何答案,干杯!
代码:
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- use IEEE.STD_LOGIC_ARITH.ALL;
-- use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;
ENTITY complement_count_test_tbw IS
END complement_count_test_tbw;
ARCHITECTURE testbench_arch OF complement_count_test_tbw IS
-- http://www.ingenieurbuero-eschemann.de/downloads/ipicregs/example/vhdl/test/timer_regs_tb.vhd
-- convert std_logic to std_logic_vector(0 downto 0)
function vectorize(s: std_logic) return std_logic_vector is
variable v: std_logic_vector(0 downto 0);
begin
v(0) := s;
return v;
end;
-- DECLARE REGISTERS ==========================
-- 'wires'
SIGNAL wtCLK : std_logic := '0';
-- counter register: 16 bit
SIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => 'Z');
-- temporary 'natural' copy of counter register
-- http://www.velocityreviews.com/forums/t21700-std_logic_vector-to-unsigned-type-casting.html
SIGNAL tmp_na : natural;
-- clock parameters
constant PERIODN : natural := 20; -- can be real := 20.0;
constant PERIOD : time := PERIODN * 1 ns;
constant DUTY_CYCLE : real := 0.5;
constant OFFSET : time := 100 ns;
-- freq divisor; with initial values
constant fdiv : natural := 16;
SIGNAL fdiv_cnt : natural := 1;
SIGNAL wfdiv_CLK : std_logic := '0';
BEGIN
-- initializations of connections:
-- instances of components, and their wiring (port maps)...
-- END instances of components, and their wiring (port maps)...
-- PROCESSES (STATE MACHINES) CODE =========
-- clock process for generating CLK
PROCESS
BEGIN
WAIT for OFFSET;
CLOCK_LOOP : LOOP
wtCLK <= '0';
-- MUST refresh counter reg here with value of tmp_na
wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));
WAIT FOR (PERIOD - (PERIOD * DUTY_CYCLE));
wtCLK <= '1';
WAIT FOR (PERIOD * DUTY_CYCLE);
END LOOP CLOCK_LOOP;
END PROCESS;
-- freq divided clock
freq_divisor: PROCESS(wtCLK)
BEGIN
IF rising_edge(wtCLK) THEN -- posedge
IF fdiv_cnt = fdiv THEN
-- reset
fdiv_cnt <= 1 ;
wfdiv_CLK <= not(wfdiv_CLK);
ELSE
fdiv_cnt <= fdiv_cnt + 1;
END IF;
END IF;
END PROCESS freq_divisor;
-- sim: count
PROCESS
BEGIN
WAIT for 10 ns;
tmp_na <= 125;
WAIT for 10 ns;
TESTCOUNT_LOOP: LOOP
-- change counter on negedge of freq. divided clock
WAIT until falling_edge(wfdiv_CLK);
tmp_na <= to_integer(unsigned(not(wCntReg))) + to_integer(unsigned(vectorize(wCntReg(15))));
WAIT for 10 ns;
END LOOP TESTCOUNT_LOOP;
END PROCESS;
-- END PROCESSES (STATE MACHINES) CODE =====
-- END IMPLEMENT ENGINE of 'CORE' ===============
END testbench_arch;
-- END ARCHITECTURE -----------------------------
Run Code Online (Sandbox Code Playgroud)
首先,避免不使用std_logic_arith的满分!
如果您将矢量定义为unsigned(在过程之外),则所需的全部内容如下:
cn_plus_1 <= not cn when cn < halfc else (not cn) + 1;
Run Code Online (Sandbox Code Playgroud)
你可以分配cn_plus_1给一个std_logic_vector:
wCntReg <= std_logic_vector(cn_plus_1);
Run Code Online (Sandbox Code Playgroud)
以下是VHDL惯用方法的几个完整示例:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity funny_counter1 is
port (
cn: IN unsigned(15 downto 0);
cn_plus_1 : out unsigned(15 downto 0));
end entity funny_counter1;
architecture a1 of funny_counter1 is
constant halfc : unsigned(cn'range) := (cn'high => '1', others => '0');
begin -- architecture a1
cn_plus_1 <= not cn when cn < halfc else (not cn) + 1;
end architecture a1;
Run Code Online (Sandbox Code Playgroud)
或者在同步过程中:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity funny_counter is
port (
clk : in std_logic;
reset : in std_logic;
cout : out unsigned(15 downto 0));
end entity funny_counter;
architecture a1 of funny_counter is
constant halfc : unsigned(cout'range) := (cout'high => '1', others => '0');
begin
process (clk) is
variable c : unsigned(15 downto 0);
variable add : integer range 0 to 1;
begin -- process
if rising_edge(clk) then -- rising clock edge
if reset = '1' then
c := (others => '0');
else
add := 0;
if c < halfc then
add := 1;
end if;
c := (not c) + add;
end if;
cout <= c;
end if;
end process;
end architecture a1;
Run Code Online (Sandbox Code Playgroud)
每当你发现自己在std_logic_vectors和integers(或类似)之间转换很多时,你通常会使用实际数字.使用unsigned/ signedvectors或整数.如果你需要一个向量,那么最后将它转换成一个对象.看起来一定不愉快.但首先询问你发送值的东西是否应该在其接口上使用某种数值类型.只有它真的是一个"袋子"才是std_logic_vector最好的类型.这个名字wCntReg对我来说就像一个计数值,所以它应该有一个数字类型.
你有这个代码:
wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));
Run Code Online (Sandbox Code Playgroud)
比它需要的稍微差一点:
wCntReg <= std_logic_vector(to_unsigned(tmp_na, wCntReg'length));
Run Code Online (Sandbox Code Playgroud)
应该工作得很好.
最后,你评论这个只在下一个时钟滴滴开始生效VHDL是如何工作的 - 信号只在一段时间过去时才更新(通常这只是在一个过程结束时).如果您想在此之前使用新值,请使用变量 - 它们会立即更新.
因此,如果您tempna是变量,您可以直接执行wCntReg任务.
为了完整性:另一种方法(通常是kludge IME)是wait for 0 ns;在信号分配之后.这会导致一些时间从更新的角度进行传递,因此所有其他增量周期将在当前时间执行(包括您想要的信号分配)时间将继续(按0ns!)并且新的增量循环可以开始.