Bidirectional bus and virtual pins

Discussion in 'VHDL' started by Shannon Gomes, Feb 3, 2007.

  1. I'm trying to write a microprossesor interface module for my project.
    It's your basic 8-bit bus with separate address bits and read / write
    strobes.

    The function of this VHDL module will be to take the data from the bus
    and stuff it into various 8-bit registers to be used by the other
    modules in my project. Of course this happens when the action is a
    'write'. On reads it will take the addressed register and put it out
    on the same 8-bit bus.

    Here is my problem: I've got over 128 bits of outputs from this
    module. The fitter complains that I don't have that many pins. Ok,
    no problem, I'll just set them up as virtual pins. However this
    setting is ignored by Quartus since all of those pins are technically
    bidirectional (remember I have to be able to write and read from them)
    and it doesn't allow bidirectional virtual pins.

    What can I do?

    Shannon
    Shannon Gomes, Feb 3, 2007
    #1
    1. Advertising

  2. Shannon Gomes

    KJ Guest

    "Shannon Gomes" <> wrote in message
    news:p...
    > I'm trying to write a microprossesor interface module for my project.
    > It's your basic 8-bit bus with separate address bits and read / write
    > strobes.
    >
    > The function of this VHDL module will be to take the data from the bus
    > and stuff it into various 8-bit registers to be used by the other
    > modules in my project. Of course this happens when the action is a
    > 'write'. On reads it will take the addressed register and put it out
    > on the same 8-bit bus.
    >
    > Here is my problem: I've got over 128 bits of outputs from this
    > module. The fitter complains that I don't have that many pins. Ok,
    > no problem, I'll just set them up as virtual pins. However this
    > setting is ignored by Quartus since all of those pins are technically
    > bidirectional (remember I have to be able to write and read from them)
    > and it doesn't allow bidirectional virtual pins.
    >
    > What can I do?
    >
    > Shannon
    KJ, Feb 4, 2007
    #2
    1. Advertising

  3. Shannon Gomes

    KJ Guest

    "Shannon Gomes" <> wrote in message
    news:p...
    > I'm trying to write a microprossesor interface module for my project.
    > It's your basic 8-bit bus with separate address bits and read / write
    > strobes.
    >
    > The function of this VHDL module will be to take the data from the bus
    > and stuff it into various 8-bit registers to be used by the other
    > modules in my project. Of course this happens when the action is a
    > 'write'. On reads it will take the addressed register and put it out
    > on the same 8-bit bus.
    >
    > Here is my problem: I've got over 128 bits of outputs from this
    > module. The fitter complains that I don't have that many pins. Ok,
    > no problem, I'll just set them up as virtual pins. However this
    > setting is ignored by Quartus since all of those pins are technically
    > bidirectional (remember I have to be able to write and read from them)
    > and it doesn't allow bidirectional virtual pins.
    >
    > What can I do?


    Write the equations that enable the 128 bits on to the 8 bits at the
    approriate time....you'll have to do it at some point.

    i.e.
    Data <= Reg1 when (Addr = 0) else
    Reg2 when (Addr = 1) else
    .....
    Reg 128 when (Addr = 127) else (others => 'Z');

    Kevin Jennings
    KJ, Feb 4, 2007
    #3
  4. Shannon Gomes

    Andy Guest

    On Feb 3, 6:59 pm, "KJ" <> wrote:
    > "Shannon Gomes" <> wrote in message
    >
    > news:p...
    >
    >
    >
    > > I'm trying to write a microprossesor interface module for my project.
    > > It's your basic 8-bit bus with separate address bits and read / write
    > > strobes.

    >
    > > The function of this VHDL module will be to take the data from the bus
    > > and stuff it into various 8-bit registers to be used by the other
    > > modules in my project. Of course this happens when the action is a
    > > 'write'. On reads it will take the addressed register and put it out
    > > on the same 8-bit bus.

    >
    > > Here is my problem: I've got over 128 bits of outputs from this
    > > module. The fitter complains that I don't have that many pins. Ok,
    > > no problem, I'll just set them up as virtual pins. However this
    > > setting is ignored by Quartus since all of those pins are technically
    > > bidirectional (remember I have to be able to write and read from them)
    > > and it doesn't allow bidirectional virtual pins.

    >
    > > What can I do?

    >
    > Write the equations that enable the 128 bits on to the 8 bits at the
    > approriate time....you'll have to do it at some point.
    >
    > i.e.
    > Data <= Reg1 when (Addr = 0) else
    > Reg2 when (Addr = 1) else
    > .....
    > Reg 128 when (Addr = 127) else (others => 'Z');
    >
    > Kevin Jennings


    Ouch That's a lot of coding, and a lot of opportunity for typo's.

    Try this tri-state bus approach (most fpga synthesizers can or will
    convert internal tri-states to muxes automatically.

    reg_type is array (0 to 127) of std_logic_vector(data'range);
    signal reg: reg_type;

    for i in reg'range generate
    data <= reg(i) when (read_en = '1') and (addr = i), else (others =>
    'Z');
    end generate;

    Or, if you want the literal mux-before-tristate instead:

    process (reg, addr) is
    variable data_tmp : std_logic_vector(data'range);
    begin
    for i in reg'range loop
    if addr = i then
    data_tmp := reg(i);
    end if;
    end loop;
    if read_en = '1' then
    data <= data_tmp;
    else
    data <= (others => 'Z');
    end if;
    end process;

    Andy
    Andy, Feb 5, 2007
    #4
  5. Shannon Gomes

    Guest

    On Sun, 04 Feb 2007 00:59:34 GMT, "KJ" <>
    wrote:

    >
    >"Shannon Gomes" <> wrote in message
    >news:p...
    >> I'm trying to write a microprossesor interface module for my project.
    >> It's your basic 8-bit bus with separate address bits and read / write
    >> strobes.
    >>
    >> The function of this VHDL module will be to take the data from the bus
    >> and stuff it into various 8-bit registers to be used by the other
    >> modules in my project. Of course this happens when the action is a
    >> 'write'. On reads it will take the addressed register and put it out
    >> on the same 8-bit bus.
    >>
    >> Here is my problem: I've got over 128 bits of outputs from this
    >> module. The fitter complains that I don't have that many pins. Ok,
    >> no problem, I'll just set them up as virtual pins. However this
    >> setting is ignored by Quartus since all of those pins are technically
    >> bidirectional (remember I have to be able to write and read from them)
    >> and it doesn't allow bidirectional virtual pins.
    >>
    >> What can I do?

    >
    >Write the equations that enable the 128 bits on to the 8 bits at the
    >approriate time....you'll have to do it at some point.
    >
    >i.e.
    >Data <= Reg1 when (Addr = 0) else
    > Reg2 when (Addr = 1) else
    > .....
    > Reg 128 when (Addr = 127) else (others => 'Z');
    >
    >Kevin Jennings
    >


    I'm not sure how that would solve my problem. I think maybe I am
    doing something so wrong that no one knows how to answer my question.

    I'll try to restate the problem:

    I have a bunch of 8 bit registers in this module. They are all
    outputs to other modules that I have written but haven't linked
    together yet. These output registers are also read by this module
    itself. Hence they are INOUT registers.

    Since there are more register bits than there are pins, when I try to
    compile this module by itself, the fitter screams that it can't fit. I
    tried to make all of the INOUT bits "virtual pins" but Quartus says it
    is ignoring that assignment. I think it is ignoring that assignment
    since they are bidirectional pins.

    I'm getting the picture that you just can't have a design of this type
    compile stand alone.

    Shannon
    , Feb 5, 2007
    #5
  6. Shannon Gomes

    Guest

    When creating a register bank, you usually have read_only regs aswell.
    And sometimes it's handy to be able to produce events from software to
    your modules using a "write_only" register, i.e. a SW write will just
    pulse
    the written outputs for a cycle.

    If you declare a type reg_def and a another type reg_defs
    as an array of reg def, you can obtain a generic regbank as follows:

    type reg_type is (read_write, read_only, write_only);
    type reg_def is record reg_addr : natural; reg_type : reg_type; end
    record reg_def;
    type reg_defs is array (natural range <>) of reg_def;
    constant my_regs : reg_defs := ((0, read_only), (16#21#, read_write));
    --
    type regs is array (my_regs'range) of std_logic_vector(7 downto 0);
    signal reg_bank : regs;

    in your read_reg process:

    for i in my_regs'range loop
    if rd = '1' and addr = my_regs(i).addr then
    data_out <= reg_bank(i);
    end if;
    end loop;

    in your write_reg process:

    for i in my_regs'range loop
    if my_regs(i).reg_type = write_only then reg_bank(i) <= (others =>
    '0'); end if; -- pulsed
    if wr = '1' and addr = my_regs(i).addr then
    case my_regs(i).reg_type
    when read_only => null; -- can't write read_only reg
    when read_write | write_only => reg_bank(i) <= data_in;
    end case;
    end if;
    end loop;

    Now connect reg_bank to your ports to the modules. (Hope the above
    compiles, but you get the idea...)

    To (try to) answer your question ;) - could you instantiate your
    interface in a top module,
    put keep attributes on all registers, then OR all output bits to one
    pin, and take all input
    bits from another pin?
    HTH /Pontus
    , Feb 5, 2007
    #6
  7. Shannon Gomes

    KJ Guest

    "Andy" <> wrote in message
    news:...
    > On Feb 3, 6:59 pm, "KJ" <> wrote:
    >> "Shannon Gomes" <> wrote in message
    >>
    >> news:p...
    >>
    >>
    >>
    >> > I'm trying to write a microprossesor interface module for my project.
    >> > It's your basic 8-bit bus with separate address bits and read / write
    >> > strobes.

    >>
    >> > The function of this VHDL module will be to take the data from the bus
    >> > and stuff it into various 8-bit registers to be used by the other
    >> > modules in my project. Of course this happens when the action is a
    >> > 'write'. On reads it will take the addressed register and put it out
    >> > on the same 8-bit bus.

    <snip>
    >>
    >> Write the equations that enable the 128 bits on to the 8 bits at the
    >> approriate time....you'll have to do it at some point.
    >>
    >> i.e.
    >> Data <= Reg1 when (Addr = 0) else
    >> Reg2 when (Addr = 1) else
    >> .....
    >> Reg 128 when (Addr = 127) else (others => 'Z');
    >>
    >> Kevin Jennings

    >
    > Ouch That's a lot of coding, and a lot of opportunity for typo's.
    >
    > Try this tri-state bus approach (most fpga synthesizers can or will

    <snip>
    For someone who is simply trying (and having obvious trouble) implementing
    read/write registers what you've posted will fly far over the head....I
    prefered to keep it a tad more straightforward to the problem at hand in the
    interest of 'walk before you can run and run before you can fly'

    Kevin Jennings
    KJ, Feb 5, 2007
    #7
  8. Shannon Gomes

    KJ Guest

    <> wrote in message
    news:...
    > On Sun, 04 Feb 2007 00:59:34 GMT, "KJ" <>
    > wrote:
    >
    >>
    >>"Shannon Gomes" <> wrote in message
    >>news:p...
    >>> I'm trying to write a microprossesor interface module for my project.
    >>> It's your basic 8-bit bus with separate address bits and read / write
    >>> strobes.
    >>>
    >>> The function of this VHDL module will be to take the data from the bus
    >>> and stuff it into various 8-bit registers to be used by the other
    >>> modules in my project. Of course this happens when the action is a
    >>> 'write'. On reads it will take the addressed register and put it out
    >>> on the same 8-bit bus.
    >>>
    >>> Here is my problem: I've got over 128 bits of outputs from this
    >>> module. The fitter complains that I don't have that many pins. Ok,
    >>> no problem, I'll just set them up as virtual pins. However this
    >>> setting is ignored by Quartus since all of those pins are technically
    >>> bidirectional (remember I have to be able to write and read from them)
    >>> and it doesn't allow bidirectional virtual pins.
    >>>
    >>> What can I do?

    >>
    >>Write the equations that enable the 128 bits on to the 8 bits at the
    >>approriate time....you'll have to do it at some point.
    >>
    >>i.e.
    >>Data <= Reg1 when (Addr = 0) else
    >> Reg2 when (Addr = 1) else
    >> .....
    >> Reg 128 when (Addr = 127) else (others => 'Z');
    >>
    >>Kevin Jennings
    >>

    >
    > I'm not sure how that would solve my problem. I think maybe I am
    > doing something so wrong that no one knows how to answer my question.
    >
    > I'll try to restate the problem:
    >
    > I have a bunch of 8 bit registers in this module. They are all
    > outputs to other modules that I have written but haven't linked
    > together yet. These output registers are also read by this module
    > itself. Hence they are INOUT registers.

    That's fine, they don't need to be 'inouts', 'out' is all that is needed
    from the individual modules. The code that instantiates the modules (i.e.
    the top level) will connect up signals to these outputs (Reg1, Reg2, etc) on
    the port map for each of these modules.

    What I was trying to imply is that you have these signals already (Reg1,
    Reg2...etc.) and they are all 8 bits wide that are presumably outputs of
    your modules. All you're trying to do by making these readable from your
    processor port is to have a way to select one of these to output on the
    processor's data bus ('Data' in my example). I did have a bit of a problem
    in my first post (corrected below). The signal Data_int selects (based on
    the processor address bus) which of your 8 bit registers you want to read
    from. Then, if the processor is actually performing a read (i.e.
    Processor_Read = '1') then output Data_int on to the signal 'Data' which is
    the only 'inout' signal in the mix, if no read is being performed, then
    'Data' is tri-stated.

    Signal 'Data' is the processor data bus which would also be connected up to
    your modules for writing to the ports inside those modules. Previously you
    indicated that you were able to get the processor bus connected
    appropriately so that you could write to all the modules, the only
    difficulty is in reading back.

    Data_int <= Reg1 when (Addr = 0) else
    Reg2 when (Addr = 1) else
    .....
    Reg 128 when (Addr = 127);
    Data <= Data_int when (Processor_Read = '1') else (others => 'Z');

    >
    > Since there are more register bits than there are pins, when I try to
    > compile this module by itself, the fitter screams that it can't fit. I
    > tried to make all of the INOUT bits "virtual pins" but Quartus says it
    > is ignoring that assignment. I think it is ignoring that assignment
    > since they are bidirectional pins.

    What you're missing is that the outputs of your various modules are NOT
    meant to be the outputs of your top level directly. The only time those
    outputs are meant to come out (as I understand it) is when the processor is
    reading from it. This means that what you're missing is the logic that
    selects the appropriate module output (the equation listed above for
    'Data_int') and then the logic to enable that selected output on to the
    actual physical input/output pins of your top level ('Data').

    Kevin Jennings
    KJ, Feb 5, 2007
    #8
  9. Shannon Gomes

    Shannon Guest

    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
    Shannon, Feb 5, 2007
    #9
  10. wrote:

    > I have a bunch of 8 bit registers in this module. They are all
    > outputs to other modules that I have written but haven't linked
    > together yet. These output registers are also read by this module
    > itself. Hence they are INOUT registers.


    I agree with Kevin.
    There are tri-buffers on device *pins*
    but register outputs and inputs are just wires.
    There are no physical tri-buffers *inside* the fpga.
    As Andy said, synthesis can infer muxes from
    an inout description, but a description using
    separate read and write data matches the
    the actual fpga hardware.

    To a slave entity, read data is a port output.
    and write data is a port input.
    To a master entity, write data is a port output
    and read data is a port input.

    An internal slave entity might might
    contain a local bus like this.

    clock : in std_ulogic;
    reset : in std_ulogic;
    address : in std_logic_vector(adr_len_g-1 downto 0);
    writeData : in std_logic_vector(char_len_g-1 downto 0);
    write_stb : in std_ulogic;
    readData : out std_logic_vector(char_len_g-1 downto 0);
    read_stb : in std_ulogic;


    Search for "stb" in the reference design here
    http://home.comcast.net/~mike_treseler/
    for an example of inferring IO registers locally.

    -- Mike Treseler
    Mike Treseler, Feb 5, 2007
    #10
  11. Shannon Gomes

    KJ Guest

    "Shannon" <> wrote in message
    news:...
    > 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:

    That helps. The only thing that jumped out at me in your post is that you
    need to separate the register writes from the register reads into separate
    processes. Something like....

    >
    > PROCESS (MClk) --KJ: Nope, you're only sensitive to MClk, not
    > nWrite --****, nWrite)
    > BEGIN
    > IF (MClk'EVENT AND MClk = '1') THEN -- Consider using the more
    > descriptive if rising_edge(MClk) then
    > IF (nWrite = '0') THEN
    > CASE Addr IS
    > WHEN "0" =>
    > REG_A <= data_in;
    > WHEN "1" =>
    > REG_B <= data_in;

    <snip, all appears to look good here>
    > WHEN OTHERS =>
    > Ignore it;
    > END CASE;

    end process; -- KJ end it here, and start another process

    process (Addr, REG_A, REG_B, ....etc.)
    begin
    -- Don't need this line, whether or not nRead is set is irrelevant >
    ELSE IF (nRead = '0') THEN
    >
    > -- NOTE: These "reads" are not legal since REG_X is declared as "OUT"

    -- KJ: Not sure what this comment is supposed to be about. Output of a
    module that is instantiated at the top level can be read. The 'REG_X'
    signals should not be direct outputs of your top level module.
    >
    > CASE Addr IS
    > WHEN "0" =>
    > data_out <= REG_A;
    > WHEN "1" =>
    > data_out <= REG_B;
    > WHEN "2" =>
    > data_out <= REG_C;

    <snip, all appears to look good here>
    > WHEN OTHERS =>
    > data_out <= (OTHERS => '0');
    > END CASE;
    > END IF;

    -- Don't need this line since the if statement is not needed END IF;
    > END PROCESS;
    > END behavioral
    >


    As you can see, the 'read process' needs to have in the sensitivity list
    each of the 'REG_X' signals along with the address to select the appropriate
    one. There are 'better' ways of handling this if you can standardize on a
    data width for all of your modules. You could define an array of
    std_logic_vectors of the appropriate width. Then the 'read process' would
    only have two signals in the sensitivity list, the address and the signal
    that is the array. Refer to Andy's post for more on that path.

    Also, on the 'read process' one of the reasons for separating it into a new
    process is that 'usually' you don't want the extra clock cycle delay that
    you would have with the way that you originally wrote it. If that extra
    'Mclk' delay doesn't matter in your situation, then you can merge it back
    into a single process. Even if you do keep it in a single process you still
    don't need the if statement that looked at 'nRead' to see if it is 0...the
    reason is simply that if 'nRead' indicates that you're not reading then it
    really doesn't matter what 'data_out' gets set to since it won't make it to
    'Data' by virtue of the (others => 'Z') portion of the equation for 'Data'.

    Kevin Jennings
    KJ, Feb 5, 2007
    #11
  12. Shannon Gomes

    Andy Guest

    On Feb 5, 11:56 am, "Shannon" <> wrote:
    > 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;
    Andy, Feb 5, 2007
    #12
  13. Shannon Gomes

    Shannon Guest

    On Feb 5, 11:25 am, "Andy" <> wrote:
    > On Feb 5, 11:56 am, "Shannon" <> wrote:
    >
    >
    >
    >
    >
    > > 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;- Hide quoted text -
    >
    > - Show quoted text -


    Wow. All I can say is wow. You've gone the extra mile to help me! I
    confess it's going to take me a little bit to absorb the information.
    I think I get it but I want to understand it completely.

    In my defense, I only intended to post some sanitized psuedo-code. It
    for sure would not compile as is. It was just for clarity reasons.

    On first blush you have addressed (pardon the pun) the two issues I
    had (I think):

    1) by using the intermediate signal "regs" I am free to read and
    write without my "INOUT" problem I refered to.
    2) after using "1" above, all my output ports can be of type "OUT"
    and there-by I can make them all virtual while I work on this module.
    3) The "loop" you wrote is nice and clean. I'm going to stare at
    that part to make sure I really understand what is happening. Since
    this is a design that will be synthesized I need to understand the
    implications at the RTL level.
    4) I completely don't understand why the "nRead" test can be removed.
    I think you (Keven in this case) were trying to say it is being
    trapped already by the "Data <=..." equation but I'm going to have to
    stare at it to really understand why.

    Again, thank you so much Kevin, Andy and Mike. I'm sure I'll be back
    with more lame questions.
    Shannon, Feb 5, 2007
    #13
  14. Shannon Gomes

    Shannon Guest

    Oh, and one last thing about the nRead timing:

    I removed a bit of code that only confuses the discusion we were
    having but answers the nRead timing problem. The true interface
    requires the micro-P to write to a "internal address" register first.
    It is that internal address register that will be providing the "Addr"
    we were talking about. So, address will be there LONG before the read
    strobe. I just removed this extra complication because it adds little
    if anything to the problem were were talking about.
    Shannon, Feb 5, 2007
    #14
  15. On 5 Feb 2007 11:25:54 -0800, "Andy" <> wrote:

    > 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;



    One last question. Why doesn't the above snipit cause there to be two
    drivers for 'data_out' when the 'if to_integer(unsigned(addr)) = i'
    clause is true?

    Shannon
    Shannon Gomes, Feb 6, 2007
    #15
  16. Shannon Gomes

    Andy Guest

    On Feb 6, 8:24 am, Shannon Gomes <> wrote:
    > On 5 Feb 2007 11:25:54 -0800, "Andy" <> wrote:
    >
    > > 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;

    >
    > One last question. Why doesn't the above snipit cause there to be two
    > drivers for 'data_out' when the 'if to_integer(unsigned(addr)) = i'
    > clause is true?
    >
    > Shannon


    Multiple drivers for a signal are created only when the signal is
    driven from multiple processes. Each process creates one driver for
    any signal it assigns, no matter how many times it may be assigned
    within an execution of that process. That single driver takes its
    value from the most recent assignment when the process suspends.
    Sequential code executes in zero simulated time, so there is no time
    for which any previous assignments (in that execution cycle) would
    take effect.

    Andy
    Andy, Feb 6, 2007
    #16
  17. Shannon Gomes

    Shannon Guest

    On Feb 6, 8:57 am, "Andy" <> wrote:
    > On Feb 6, 8:24 am, Shannon Gomes <> wrote:
    >
    >
    >
    >
    >
    > > On 5 Feb 2007 11:25:54 -0800, "Andy" <> wrote:

    >
    > > > 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;

    >
    > > One last question. Why doesn't the above snipit cause there to be two
    > > drivers for 'data_out' when the 'if to_integer(unsigned(addr)) = i'
    > > clause is true?

    >
    > > Shannon

    >
    > Multiple drivers for a signal are created only when the signal is
    > driven from multiple processes. Each process creates one driver for
    > any signal it assigns, no matter how many times it may be assigned
    > within an execution of that process. That single driver takes its
    > value from the most recent assignment when the process suspends.
    > Sequential code executes in zero simulated time, so there is no time
    > for which any previous assignments (in that execution cycle) would
    > take effect.
    >
    > Andy- Hide quoted text -
    >
    > - Show quoted text -


    Thanks. I'm a hardware guy. I always think about things from a
    hardware perspective. I guess I just don't naturally think of
    "sequential" logic. I should probably compile that code and see what
    the RTL viewer says.
    Shannon, Feb 6, 2007
    #17
  18. Shannon wrote:

    > I always think about things from a
    > hardware perspective. I guess I just don't naturally think of
    > "sequential" logic.


    That's a sequential hardware *description*.

    Such a process often starts with
    a default assignment for the most common case
    and alternate assignments for the other cases.
    The point is that the assignments don't interact.
    The last assignment traced by the code wins.

    I should probably compile that code and see what
    > the RTL viewer says.


    That's the spirit.

    -- Mike Treseler
    Mike Treseler, Feb 6, 2007
    #18
  19. Shannon Gomes

    Andy Guest

    On Feb 6, 2:53 pm, "Shannon" <> wrote:
    > On Feb 6, 8:57 am, "Andy" <> wrote:
    >
    >
    >
    > > On Feb 6, 8:24 am, Shannon Gomes <> wrote:

    >
    > > > On 5 Feb 2007 11:25:54 -0800, "Andy" <> wrote:

    >
    > > > > 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;

    >
    > > > One last question. Why doesn't the above snipit cause there to be two
    > > > drivers for 'data_out' when the 'if to_integer(unsigned(addr)) = i'
    > > > clause is true?

    >
    > > > Shannon

    >
    > > Multiple drivers for a signal are created only when the signal is
    > > driven from multiple processes. Each process creates one driver for
    > > any signal it assigns, no matter how many times it may be assigned
    > > within an execution of that process. That single driver takes its
    > > value from the most recent assignment when the process suspends.
    > > Sequential code executes in zero simulated time, so there is no time
    > > for which any previous assignments (in that execution cycle) would
    > > take effect.

    >
    > > Andy- Hide quoted text -

    >
    > > - Show quoted text -

    >
    > Thanks. I'm a hardware guy. I always think about things from a
    > hardware perspective. I guess I just don't naturally think of
    > "sequential" logic. I should probably compile that code and see what
    > the RTL viewer says.


    That (using the RTL viewer) is an excellent way to learn what does
    what in terms of code to hardware.

    Andy
    Andy, Feb 7, 2007
    #19
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. ALuPin

    SRAM bidirectional bus

    ALuPin, Feb 24, 2004, in forum: VHDL
    Replies:
    6
    Views:
    4,655
    bittor
    Dec 22, 2004
  2. Don Golding
    Replies:
    2
    Views:
    509
    Leon Heller
    Apr 15, 2004
  3. Don Golding
    Replies:
    0
    Views:
    618
    Don Golding
    Apr 23, 2004
  4. Drew
    Replies:
    1
    Views:
    517
    wieli1
    Jul 26, 2004
  5. Manfred Balik
    Replies:
    12
    Views:
    6,552
    Marc Guardiani
    Sep 10, 2006
Loading...

Share This Page