Thanks for all the wonderful responses. I think we are getting close
to the solution. What I've decided to do is post some psuedo code to
show what it is I'm trying to do:
ENTITY xFace IS
PORT
(
Addr : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
Data : INOUT STD_LOGIC_VECTOR(7 DOWNTO 0);
nRead : IN STD_LOGIC;
nWrite : IN STD_LOGIC;
MClk : IN STD_LOGIC;
-- These registers are outputs only for this module. When this module
gets hooked
-- with the others under another top-level then these "output ports"
will be hooked
-- to "input ports" of the other modules.
REG_A : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
REG_B : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
REG_C : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
REG_D : OUT STD_LOGIC_VECTOR(31 DOWNTO 0);
REG_E : OUT STD_LOGIC_VECTOR(31 DOWNTO 0);
REG_F : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
);
END xFace;
ARCHITECTURE behavioral OF xFace IS
SIGNAL data_in : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL data_out : STD_LOGIC_VECTOR(7 DOWNTO 0);
BEGIN
data_in <= Data;
Data <= data_out WHEN nRead = '0' ELSE (OTHERS => 'Z');
PROCESS (MClk, nWrite)
BEGIN
IF (MClk'EVENT AND MClk = '1') THEN
IF (nWrite = '0') THEN
CASE Addr IS
WHEN "0" =>
REG_A <= data_in;
WHEN "1" =>
REG_B <= data_in;
WHEN "2" =>
REG_C <= data_in;
WHEN "3" =>
REG_D(7 DOWNTO 0) <= data_in;
WHEN "4" =>
REG_D(15 DOWNTO 8) <= data_in;
WHEN "5" =>
REG_D(23 DOWNTO 16) <= data_in;
WHEN "6" =>
REG_D(31 DOWNTO 24) <= data_in;
WHEN "7" =>
REG_E(7 DOWNTO 0) <= data_in;
WHEN "8" =>
REG_E(15 DOWNTO 8) <= data_in;
WHEN "9" =>
REG_E(23 DOWNTO 16) <= data_in;
WHEN "10" =>
REG_E(31 DOWNTO 24) <= data_in;
WHEN "11" =>
REG_F(7 DOWNTO 0) <= data_in;
WHEN "12" =>
REG_F(15 DOWNTO 8) <= data_in;
WHEN OTHERS =>
Ignore it;
END CASE;
ELSE IF (nRead = '0') THEN
-- NOTE: These "reads" are not legal since REG_X is declared as "OUT"
CASE Addr IS
WHEN "0" =>
data_out <= REG_A;
WHEN "1" =>
data_out <= REG_B;
WHEN "2" =>
data_out <= REG_C;
WHEN "3" =>
data_out <= REG_D(7 DOWNTO 0);
WHEN "4" =>
data_out <= REG_D(15 DOWNTO 8);
WHEN "5" =>
data_out <= REG_D(23 DOWNTO 16);
WHEN "6" =>
data_out <= REG_D(31 DOWNTO 24);
WHEN "7" =>
data_out <= REG_E(7 DOWNTO 0);
WHEN "8" =>
data_out <= REG_E(15 DOWNTO 8);
WHEN "9" =>
data_out <= REG_E(23 DOWNTO 16);
WHEN "10" =>
data_out <= REG_E(31 DOWNTO 24);
WHEN "11" =>
data_out <= REG_F(7 DOWNTO 0);
WHEN "12" =>
data_out <= REG_F(15 DOWNTO 8);
WHEN OTHERS =>
data_out <= (OTHERS => '0');
END CASE;
END IF;
END IF;
END PROCESS;
END behavioral
Thank you all for being so patient with a noob.
Shannon
You can allow reading the reg_x outputs if you have an intermediate
signal/variable to handle the data, and read it back instead.
You can also simplify your addressing, and ensure that read addressing
works the same as write addressing, by using an array of bytes for
that intermediate signal/variable.
Whenever I see a long case statement comparing an address or index
against a sequence of numeric values, I think "Can I replace that with
an array and a loop?" Loops are unrolled in synthesis, so the index
becomes effectively "static" (not from a language point of view, but
there is no computation that must be implemented in hardware to index
the loop.
Some think that such "advance topics" are not for the beginner... I
think the sooner you learn loops and arrays, the better, and this is
an excellent example of where they can be used to reduce code bulk
(and typing!) while improving the reliability and maintainability of
the code. For instance, if you needed to add a reg_x port (or take one
away), you simply adjust the size of reg_type, and add/delete the
assignment(s) of the port from the regs array; done!
As written, your case statement "when" targets are not of type
std_logic_vector, and thus would not compile.
Also, I'll leave it up to you to figure out how to handle the fact
that data_out does not get updated until _after_ the clock cycle in
which nRead is '0', yet you are driving the data from data_out in the
same clock cycle as when it is '0'. If nRead is always on for at least
2 clocks, and the data will not be latched by whoever is reading it
until after the 1st clock, then this will work as is.
Andy
use ieee.numeric_std.all;
architecture rtl of xFace is
type reg_type : array (0 to 12) of std_logic_vector(data'range);
signal regs : reg_type;
signal data_in, data_out : std_logic_vector(data'range);
begin
data_in <= Data;
Data <= data_out WHEN nRead = '0' ELSE (OTHERS => 'Z');
PROCESS (MClk) -- nWrite not needed in sens. list
BEGIN
IF rising_edge(MClk) THEN
IF (nWrite = '0') THEN
for i in regs'range loop
if to_integer(unsigned(addr)) = i then
regs(i) <= data_in;
end if;
end loop;
ELSE IF (nRead = '0') THEN
data_out <= (others => '0');
for i in regs'range loop
if to_integer(unsigned(addr)) = i then
data_out <= regs(i);
end if;
end loop;
END IF;
END IF;
END PROCESS;
-- Assign output ports:
REG_A <= regs(0);
REG_B <= regs(1);
REG_C <= regs(2);
REG_D(7 DOWNTO 0) <= regs(3);
REG_D(15 DOWNTO 8) <= regs(4);
REG_D(23 DOWNTO 16) <= regs(5);
REG_D(31 DOWNTO 24) <= regs(6);
REG_E(7 DOWNTO 0) <= regs(7);
REG_E(15 DOWNTO 8) <= regs(8);
REG_E(23 DOWNTO 16) <= regs(9);
REG_E(31 DOWNTO 24) <= regs(10);
REG_F(7 DOWNTO 0) <= regs(11);
REG_F(15 DOWNTO 8) <= regs(12);
end architecture rtl;