M
M. Norton
Well, the IDE vs emacs vs vi vs writing everything out by hand in
pencil vs adjusting the universal constants to create cosmic rays that
strikes the hard disk platter in just the right way to write a one or
zero discussion has been entertaining, but though I might throw out
something more designish.
I've got a structure in this Arria II GX device where I've got a PCI
core backend bus (from an Actel) doing its thing with memory in the
Arria. Some memory regions are large enough where I just use the
altsyncram function from Quartus to implement the large scale memory.
I've got a few little discrete registers laying around where it
doesn't seem to make sense to implement large components and I thought
I'd write my own little cross-clock domain protected register. In
this case, the PCI backend is 33 MHz and this little peripheral SPI
interface is 10 MHz. The registers are supposed to be just uni-
directional -- haven't decided to implement anything more elaborate
just yet.
The problem is, I think I'm missing something kind of vital.
Especially when both clocks are incident at just about the same time
and I'm wondering if I need metastability registers in between to aid
in the data transfer. Perhaps only the request/ack registers need
metastability. Anyhow, this is what I've got and if it amuses someone
to take a look and comment (or laugh, I'm good with that too), I'd be
very interested in outside thoughts. The basic idea is that clock
domain A writes an internal register, and raises a request flag.
Clock domain B, as long as it's not being read from, sees the request
and registers the data and raises it's acknowledge flag. Clock domain
A sees the acknowledge and lowers its request. Clock domain B sees
the request rescind and lowers its acknowledge, and we're back to
normal. It seems to me like that ought to carefully lock the data
through the clock domain without anything getting dropped... but
something is still tickling at the back of my brain and I can't quite
decide if there's a problem or not.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity xckd_reg is
generic (
WIDTH : integer := 32
);
port (
reset : in std_logic;
clk_a : in std_logic;
d_a : in std_logic_vector(WIDTH-1 downto 0);
wren_a : in std_logic;
clk_b : in std_logic;
rden_b : in std_logic;
q_b : out std_logic_vector(WIDTH-1 downto 0)
);
end entity xckd_reg;
architecture rtl of xckd_reg is
signal reg_a : std_logic_vector(WIDTH-1 downto 0);
signal req : std_logic;
signal ack : std_logic;
begin
process (clk_a, clk_b, reset)
begin
if (reset = '1') then
reg_a <= (others => '0');
req <= '0';
elsif rising_edge(clk_a) then
if (wren_a = '1' and req /= '1') then
reg_a <= d_a;
req <= '1';
elsif (req = '1' and ack = '1') then
req <= '0';
end if;
end if;
if (reset = '1') then
q_b <= (others => '0');
elsif rising_edge(clk_b) then
if (req = '1' and ack = '0' and rden_b /= '1') then
q_b <= reg_a;
ack <= '1';
elsif (req = '0' and ack = '1') then
ack <= '0';
end if;
end if;
end process;
end architecture rtl;
pencil vs adjusting the universal constants to create cosmic rays that
strikes the hard disk platter in just the right way to write a one or
zero discussion has been entertaining, but though I might throw out
something more designish.
I've got a structure in this Arria II GX device where I've got a PCI
core backend bus (from an Actel) doing its thing with memory in the
Arria. Some memory regions are large enough where I just use the
altsyncram function from Quartus to implement the large scale memory.
I've got a few little discrete registers laying around where it
doesn't seem to make sense to implement large components and I thought
I'd write my own little cross-clock domain protected register. In
this case, the PCI backend is 33 MHz and this little peripheral SPI
interface is 10 MHz. The registers are supposed to be just uni-
directional -- haven't decided to implement anything more elaborate
just yet.
The problem is, I think I'm missing something kind of vital.
Especially when both clocks are incident at just about the same time
and I'm wondering if I need metastability registers in between to aid
in the data transfer. Perhaps only the request/ack registers need
metastability. Anyhow, this is what I've got and if it amuses someone
to take a look and comment (or laugh, I'm good with that too), I'd be
very interested in outside thoughts. The basic idea is that clock
domain A writes an internal register, and raises a request flag.
Clock domain B, as long as it's not being read from, sees the request
and registers the data and raises it's acknowledge flag. Clock domain
A sees the acknowledge and lowers its request. Clock domain B sees
the request rescind and lowers its acknowledge, and we're back to
normal. It seems to me like that ought to carefully lock the data
through the clock domain without anything getting dropped... but
something is still tickling at the back of my brain and I can't quite
decide if there's a problem or not.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity xckd_reg is
generic (
WIDTH : integer := 32
);
port (
reset : in std_logic;
clk_a : in std_logic;
d_a : in std_logic_vector(WIDTH-1 downto 0);
wren_a : in std_logic;
clk_b : in std_logic;
rden_b : in std_logic;
q_b : out std_logic_vector(WIDTH-1 downto 0)
);
end entity xckd_reg;
architecture rtl of xckd_reg is
signal reg_a : std_logic_vector(WIDTH-1 downto 0);
signal req : std_logic;
signal ack : std_logic;
begin
process (clk_a, clk_b, reset)
begin
if (reset = '1') then
reg_a <= (others => '0');
req <= '0';
elsif rising_edge(clk_a) then
if (wren_a = '1' and req /= '1') then
reg_a <= d_a;
req <= '1';
elsif (req = '1' and ack = '1') then
req <= '0';
end if;
end if;
if (reset = '1') then
q_b <= (others => '0');
elsif rising_edge(clk_b) then
if (req = '1' and ack = '0' and rden_b /= '1') then
q_b <= reg_a;
ack <= '1';
elsif (req = '0' and ack = '1') then
ack <= '0';
end if;
end if;
end process;
end architecture rtl;