问题
I am working on generating a 40 bit length pulse train. I also must be able to adjust the frequency. I tried to make a new low frequency clock and i make a new counter which counts on it's rising edges and give an high output and terminating after 40 bit. It's not working. I tried some other methods. They are not, too.
For example;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.all;
entity con40 is port(clk:in std_ulogic; q:out std_ulogic);
end entity con40;
architecture Behaviour of con40 is
constant s:std_ulogic_vector:="11111111111111111111111111111111";
signal i:unsigned(4 downto 0):="00000";
signal en:std_logic:='1';
signal reset:std_logic:='0';
begin
q<=s(to_integer(i));
process(reset,clk) is begin
if reset='1' then
i<=(others=>'0');
elsif rising_edge(clk) then
if en='1' then
i<=i+1;
end if;
end if;
end process;
end architecture Behaviour;
There is 32-bit length in this code but i wanna make 40 bit but whatever, this is not working too. I think methods for such a pulse train must be common and they are being used widely. But hey! unluckily i can find nothing useful.
回答1:
I took the liberty of moving en
and reset
to port signals, also changed your constant to a recognizable 40 bit value, and specified the range to make it a locally static constant.
The issue with your counter is that it isn't big enough to address 40 bits. You have i
specified as a 5 bit value while 40 bits requires a 6 bit counter.
I also added a second architecture here with i as an integer type signal. With i
as either an unsigned value or an integer type you likely need to roll over the i
counter at 39 ("100111"
) when the first position is 0 ("000000"
).
library ieee;
use ieee.std_logic_1164.all;
entity con40 is
port(
reset: in std_ulogic;
clk: in std_ulogic;
en: in std_ulogic;
q: out std_ulogic
);
end entity con40;
architecture foo of con40 is
constant s: std_ulogic_vector (0 to 39) := x"feedfacedb";
signal i: natural range 0 to 39;
begin
q <= s(i);
process (reset, clk)
begin
if reset = '1' then
i <= 0;
elsif rising_edge(clk) and en = '1' then
if i = 39 then
i <= 0;
else
i <= i + 1;
end if;
end if;
end process;
end architecture;
library ieee;
use ieee.numeric_std.all;
architecture behave of con40 is
constant s: std_ulogic_vector (0 to 39) := x"feedfacedb";
signal i: unsigned (5 downto 0);
begin
q <= s(to_integer(i));
process (reset, clk)
begin
if reset = '1' then
i <= "000000";
elsif rising_edge(clk) and en = '1' then
if i = "100111" then
i <= "000000";
else
i <= i + 1;
end if;
end if;
end process;
end architecture;
I also did a quick and dirty test bench:
library ieee;
use ieee.std_logic_1164.all;
entity tb_con40 is
end entity;
architecture foo of tb_con40 is
signal clk: std_ulogic := '0';
signal reset: std_ulogic := '1';
signal en: std_ulogic := '0';
signal q: std_ulogic;
begin
DUT:
entity work.con40
port map (
reset => reset,
clk => clk,
en => en,
q => q
);
CLOCK:
process
begin
for i in 0 to 46 loop
wait for 20 ns;
clk <= not clk;
wait for 20 ns;
clk <= not clk;
end loop;
wait;
end process;
STIMULUS1:
reset <= '0' after 40 ns;
STIMULUS2:
en <= '1' after 60 ns;
end architecture;
Which can demonstrate the correct output:
addendum in response to comment question
The pattern X"FEEDFACEDB"
is 40 bits long and was substituted for the 32 all '1's value for constant s
to demonstrate that you are actually addressing individual elements of the s
array value.
To stop the pulse train fro recurring:
For architecture foo
(using an integer type for i
):
elsif rising_edge(clk) and en = '1' then
-- if i = 39 then
-- i <= 0;
-- else
if i /= 39 then -- added
i <= i + 1;
end if;
This stops the counter from operating when it reaches 39.
For architecture behave (using an unsigned type for i
):
elsif rising_edge(clk) and en = '1' then
-- if i = "100111" then
-- i <= "000000";
-- else
if i /= "100111" then -- added
i <= i + 1;
end if;
end if;
Both architectures behave identically stopping the i
counter at 39 ("100111"
).
The counter can be shown to have stopped by simulating:
Without adding an additional control input the only way to get the pulse stream to occur a second time would be by invoking reseet.
回答2:
The following code could be a simple implementation to generate pulse trains. This module requires a start impulse (StartSequence) and acknowledges the generated sequence with 'SequenceCompleted'.
Instead of an state machine I use a basic RS flip flop with set = StartSequence
and rst = SequenceCompleted_i
. I also broke up the process into two processes:
- state control - this can be extended to a full FSM if needed
- used for counter(s)
Initially, the module emits PULSE_TRAIN(0) by default and also after each sequence generation. So if you want to emit 40 ones otherwise zero set PULSE_TRAIN := (0 => '0', 1 to 40 => '1')
This module is variable in the bit count of PULSE_TRAIN, so I needed to include a function called log2ceil, which calculates the 2s logarithm aka needed bits from PULSE_TRAIN's length attribute.
So in case of 'length = 41
bits Counter_us
has a range of (5 downto 0).
entity PulseTrain is
generic (
PULSE_TRAIN : STD_LOGIC_VECTOR
);
port (
Clock : in STD_LOGIC;
StartSequence : in STD_LOGIC;
SequenceCompleted : out STD_LOGIC;
Output : out STD_LOGIC
);
end entity;
architecture rtl of PulseTrain is
function log2ceil(arg : POSITIVE) return NATURAL is
variable tmp : POSITIVE := 1;
variable log : NATURAL := 0;
begin
if arg = 1 then return 0; end if;
while arg > tmp loop
tmp := tmp * 2;
log := log + 1;
end loop;
return log;
end function;
signal State : STD_LOGIC := '0';
signal Counter_us : UNSIGNED(log2ceil(PULSE_TRAIN'length) - 1 downto 0) := (others => '0');
signal SequenceCompleted_i : STD_LOGIC;
begin
process(Clock) is
begin
if rising_edge(Clock) then
if (StartSequence = '1') then
State <= '1';
elsif (SequenceCompleted_i = '1') then
State <= '0';
end if;
end if;
end process;
SequenceCompleted_i <= '1' when (Counter_us = (PULSE_TRAIN'length - 1)) else '0';
SequenceCompleted <= SequenceCompleted_i;
process(Clock)
begin
if rising_edge(Clock) then
if (State = '0') then
Counter_us <= (others => '0');
else
Counter_us <= Counter_us + 1;
end if;
end if;
end process;
Output <= PULSE_TRAIN(to_integer(Counter_us));
end;
回答3:
As what @fru1tbat mentioned, it's not really clear what is "not working" and what you really intend to do. If you would really just want to generate a pulse train, one would think you want to generate a series of alternating '1' and '0', not all '1's like in the code you posted.
Also, the i
counter just counts up, and can only be reset to '0' by use of the reset
signal, which is fine as long as you intended it that way.
If you'd like to generate a train of '1's and '0's, you'd need something like this (not tested, but should be along these lines):
architecture behaviour of con40 is
constant trainLength:positive:=80;
signal i:unsigned(6 downto 0):=(others=>'0');
...
begin
process(reset,clk) is begin
if reset then
i<=(others=>'0');
q<='0';
elsif rising_edge(clk) then
q<='0'; -- default assignment.
-- Defaults to '0' when if-statement fails.
if i<trainLength then
i<=i+1;
q<=not q;
end if;
end if;
end process;
end architecture behaviour;
This gives you a single-shot pulse train, means there is no way to repeat generation of the pulse train unless you assert the reset
signal again. This is fine if it's what you want, otherwise, you'll need more signals to cater for cases where you'd like to re-generate the pulse train without resetting.
Here, I'm assuming you'd like 40 HIGH pulses, which essentially makes the train length 80 clock cycles, not 40. Also, I'm assuming you want a 50% duty cycle, i.e. the HIGH and LOW times are equal. Depending on your requirements, you may need a pulse width that is longer or shorter.
With these assumptions in mind, you'd need at least a 7-bit counter to count 80 clocks. You may think of other better ways to do this as well, but this just comes off the top of my head, and is probably a good place to start.
If your tool doesn't yet support VHDL-2008's enhanced port modes (e.g. ability to read from out
-mode ports), then you could declare q
as having a buffer
mode instead of out
. If your tool doesn't support buffer
port modes, then you can declare an internal signal and use it for your logic. E.g.:
signal i_q: std_ulogic;
...
i_q<=not i_q; -- use internal signal for logic instead.
q<=i_q; -- drive output from internal signal.
To adjust the frequency, simply supply a higher or lower frequency into your clk
input. This can be generated from another PLL, or a frequency divider, or any other oscillating circuitry you have available. Just supply its output into your clk
.
Hope this helps.
来源:https://stackoverflow.com/questions/25783320/how-can-i-generate-a-pulse-train-to-give-output-in-common-way